ASP.NET Nhận dạng DbContext nhầm lẫn


196

Ứng dụng MVC 5 mặc định đi kèm với đoạn mã này trong IdentityModels.cs - đoạn mã này dành cho tất cả các hoạt động Nhận dạng ASP.NET cho các mẫu mặc định:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
}

Nếu tôi tạo ra một bộ điều khiển mới bằng cách sử dụng các khung nhìn với Entity Framework và tạo một "bối cảnh dữ liệu mới ..." trong hộp thoại, tôi sẽ tạo ra điều này cho tôi:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace WebApplication1.Models
{
    public class AllTheOtherStuffDbContext : DbContext
    {
        // You can add custom code to this file. Changes will not be overwritten.
        // 
        // If you want Entity Framework to drop and regenerate your database
        // automatically whenever you change your model schema, please use data migrations.
        // For more information refer to the documentation:
        // http://msdn.microsoft.com/en-us/data/jj591621.aspx

        public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext")
        {
        }

        public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }

    }
} 

Nếu tôi tạo ra một bộ điều khiển + khung nhìn khác bằng cách sử dụng EF, ví dụ như đối với mô hình Động vật, dòng mới này sẽ được tự động tạo ngay bên dưới public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }- như thế này:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace WebApplication1.Models
{
    public class AllTheOtherStuffDbContext : DbContext
    {
        // You can add custom code to this file. Changes will not be overwritten.
        // 
        // If you want Entity Framework to drop and regenerate your database
        // automatically whenever you change your model schema, please use data migrations.
        // For more information refer to the documentation:
        // http://msdn.microsoft.com/en-us/data/jj591621.aspx

        public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext")
        {
        }

        public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }
        public System.Data.Entity.DbSet<WebApplication1.Models.Animal> Animals { get; set; }

    }
} 

ApplicationDbContext(đối với tất cả các công cụ Nhận dạng ASP.NET) kế thừa từ IdentityDbContextđó lần lượt thừa hưởng từ đó DbContext. AllOtherStuffDbContext(cho nội dung của riêng tôi) thừa hưởng từDbContext .

Vì vậy, câu hỏi của tôi là:

Tôi nên sử dụng loại nào trong hai ( ApplicationDbContextAllOtherStuffDbContext) cho tất cả các mẫu khác của mình? Hoặc tôi chỉ nên sử dụng mặc định được tạo tự động ApplicationDbContextvì nó không phải là vấn đề khi sử dụng nó vì nó xuất phát từ lớp cơ sở DbContext, hoặc sẽ có một số chi phí? Bạn chỉ nên sử dụng một DbContextđối tượng trong ứng dụng cho tất cả các kiểu máy của mình (tôi đã đọc nó ở đâu đó) vì vậy tôi thậm chí không nên xem xét sử dụng cả hai ApplicationDbContextAllOtherStuffDbContexttrong một ứng dụng? Hoặc thực hành tốt nhất trong MVC 5 với ASP.NET Identity là gì?


1
Nhân tiện; Điều này thật khó hiểu và không cần thiết cho mắt tôi khi quét tài liệu: công khai System.Data.Entity.Dbset <WebApplication1.Models.Movie> Phim {get; bộ; } - phần System.Data.Entity và WebApplication1.Models. Không thể xóa nó khỏi khai báo và thay vào đó thêm các không gian tên trong phần sử dụng câu lệnh?
PussInBoots

Puss - có để bình luận của bạn. Điều đó sẽ làm việc tốt.
SB2055

Đây là một ví dụ tốt và hoạt động, ví dụ (MVC 6) và lib khi triển khai với khung ASP.NET 5 Identity (> = v3) không có Entity Framework cho MongoDB.Driver (> = v2.1.0) github.com/saan800/SaanSoft. AspNet.Identity3.MongoDB
Stanislav Prusac

Câu trả lời:


178

Tôi sẽ sử dụng một lớp Ngữ cảnh duy nhất kế thừa từ IdentityDbContext. Bằng cách này, bạn có thể có bối cảnh nhận thức được bất kỳ mối quan hệ nào giữa các lớp của bạn và IdentityUser và Vai trò của IdentityDbContext. Có rất ít chi phí trong IdentityDbContext, về cơ bản nó là một DbContext thông thường với hai DbSets. Một cho người dùng và một cho các vai trò.


52
Đó là cho một dự án MVC5 nhưng không mong muốn khi DbContext dẫn xuất được chia sẻ giữa nhiều dự án, một số không phải MVC5, trong đó một số không cần hỗ trợ Nhận dạng.
Dave

Bình chọn cho cùng một cơ sở dữ liệu để dễ bảo trì hơn và tính toàn vẹn quan hệ tốt hơn. Bởi vì thực thể người dùng và thực thể vai trò sẽ dễ dàng liên quan đến các đối tượng ứng dụng khác.
anIBMer

6
@Dave - Nó làm phức tạp việc phân vùng dữ liệu người dùng bằng cách sử dụng hai bối cảnh khác nhau. Có phải dữ liệu phân vùng ứng dụng MVC của bạn theo người dùng nhưng các ứng dụng khác thì không. Chia sẻ cùng một lớp dữ liệu là phổ biến, nhưng tôi không nghĩ rằng một số dự án cần dữ liệu được chia theo người dùng và một số thì không.
RickAndMSFT

1
Có ai biết cách giải nén ApplicationDBContext từ một dự án MVC và đưa nó vào một lớp dữ liệu EF hiện có không? Hợp nhất cả hai, như được mô tả ở trên dường như là cách tiếp cận phù hợp, nhưng tôi đang làm việc trong một dự án bị hạn chế về thời gian. Tôi muốn làm điều đó ngay lần đầu tiên thông qua nhưng sẽ yêu thích một cái đầu lên trên tất cả các vấn đề nằm trước mặt tôi ...
Mike Devenney 20/03/2015

7
Sau khi tìm kiếm khoảng một giờ, câu trả lời này đã chỉ cho tôi đi đúng hướng - nhưng tôi không chắc chắn làm thế nào để thực hiện nó (đối với một người rất biết chữ tôi). Vì vậy, nếu nó giúp được người khác, tôi đã tìm ra cách đơn giản nhất là mở IdentityModels.cs và thêm Dbset mới của bạn vào lớp ApplicationDbContext.
SeanOB

45

Có rất nhiều nhầm lẫn về IdentityDbContext , một tìm kiếm nhanh trong Stackoverflow và bạn sẽ tìm thấy những câu hỏi sau:
" Tại sao Asp.Net Nhận dạng Nhận dạngDbContext một Hộp đen?
Làm cách nào tôi có thể thay đổi tên bảng khi sử dụng Visual Studio 2013
Hợp nhất MyDbContext với IdentityDbContext "

Để trả lời cho tất cả những câu hỏi này, chúng ta cần hiểu rằng IdentityDbContext chỉ là một lớp được kế thừa từ DbContext.
Chúng ta hãy xem nguồn của IdentityDbContext :

/// <summary>
/// Base class for the Entity Framework database context used for identity.
/// </summary>
/// <typeparam name="TUser">The type of user objects.</typeparam>
/// <typeparam name="TRole">The type of role objects.</typeparam>
/// <typeparam name="TKey">The type of the primary key for users and roles.</typeparam>
/// <typeparam name="TUserClaim">The type of the user claim object.</typeparam>
/// <typeparam name="TUserRole">The type of the user role object.</typeparam>
/// <typeparam name="TUserLogin">The type of the user login object.</typeparam>
/// <typeparam name="TRoleClaim">The type of the role claim object.</typeparam>
/// <typeparam name="TUserToken">The type of the user token object.</typeparam>
public abstract class IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken> : DbContext
    where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin>
    where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
    where TKey : IEquatable<TKey>
    where TUserClaim : IdentityUserClaim<TKey>
    where TUserRole : IdentityUserRole<TKey>
    where TUserLogin : IdentityUserLogin<TKey>
    where TRoleClaim : IdentityRoleClaim<TKey>
    where TUserToken : IdentityUserToken<TKey>
{
    /// <summary>
    /// Initializes a new instance of <see cref="IdentityDbContext"/>.
    /// </summary>
    /// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>
    public IdentityDbContext(DbContextOptions options) : base(options)
    { }

    /// <summary>
    /// Initializes a new instance of the <see cref="IdentityDbContext" /> class.
    /// </summary>
    protected IdentityDbContext()
    { }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of Users.
    /// </summary>
    public DbSet<TUser> Users { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of User claims.
    /// </summary>
    public DbSet<TUserClaim> UserClaims { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of User logins.
    /// </summary>
    public DbSet<TUserLogin> UserLogins { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of User roles.
    /// </summary>
    public DbSet<TUserRole> UserRoles { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of User tokens.
    /// </summary>
    public DbSet<TUserToken> UserTokens { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of roles.
    /// </summary>
    public DbSet<TRole> Roles { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of role claims.
    /// </summary>
    public DbSet<TRoleClaim> RoleClaims { get; set; }

    /// <summary>
    /// Configures the schema needed for the identity framework.
    /// </summary>
    /// <param name="builder">
    /// The builder being used to construct the model for this context.
    /// </param>
    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<TUser>(b =>
        {
            b.HasKey(u => u.Id);
            b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique();
            b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex");
            b.ToTable("AspNetUsers");
            b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();

            b.Property(u => u.UserName).HasMaxLength(256);
            b.Property(u => u.NormalizedUserName).HasMaxLength(256);
            b.Property(u => u.Email).HasMaxLength(256);
            b.Property(u => u.NormalizedEmail).HasMaxLength(256);
            b.HasMany(u => u.Claims).WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
            b.HasMany(u => u.Logins).WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
            b.HasMany(u => u.Roles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
        });

        builder.Entity<TRole>(b =>
        {
            b.HasKey(r => r.Id);
            b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex");
            b.ToTable("AspNetRoles");
            b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();

            b.Property(u => u.Name).HasMaxLength(256);
            b.Property(u => u.NormalizedName).HasMaxLength(256);

            b.HasMany(r => r.Users).WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
            b.HasMany(r => r.Claims).WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
        });

        builder.Entity<TUserClaim>(b => 
        {
            b.HasKey(uc => uc.Id);
            b.ToTable("AspNetUserClaims");
        });

        builder.Entity<TRoleClaim>(b => 
        {
            b.HasKey(rc => rc.Id);
            b.ToTable("AspNetRoleClaims");
        });

        builder.Entity<TUserRole>(b => 
        {
            b.HasKey(r => new { r.UserId, r.RoleId });
            b.ToTable("AspNetUserRoles");
        });

        builder.Entity<TUserLogin>(b =>
        {
            b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
            b.ToTable("AspNetUserLogins");
        });

        builder.Entity<TUserToken>(b => 
        {
            b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name });
            b.ToTable("AspNetUserTokens");
        });
    }
}


Dựa trên mã nguồn nếu chúng ta muốn hợp nhất IdentityDbContext với DbContext của chúng ta, chúng ta có hai tùy chọn:

Tùy chọn thứ nhất:
Tạo một DbContext kế thừa từ IdentityDbContext và có quyền truy cập vào các lớp.

   public class ApplicationDbContext 
    : IdentityDbContext
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }

    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    // Add additional items here as needed
}


Ghi chú thêm:

1) Chúng tôi cũng có thể thay đổi tên bảng mặc định danh tính asp.net bằng giải pháp sau:

    public class ApplicationDbContext : IdentityDbContext
    {    
        public ApplicationDbContext(): base("DefaultConnection")
        {
        }

        protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<IdentityUser>().ToTable("user");
            modelBuilder.Entity<ApplicationUser>().ToTable("user");

            modelBuilder.Entity<IdentityRole>().ToTable("role");
            modelBuilder.Entity<IdentityUserRole>().ToTable("userrole");
            modelBuilder.Entity<IdentityUserClaim>().ToTable("userclaim");
            modelBuilder.Entity<IdentityUserLogin>().ToTable("userlogin");
        }
    }

2) Hơn nữa, chúng tôi có thể mở rộng từng lớp và thêm bất kỳ thuộc tính nào vào các lớp như 'IdentityUser', 'IdentityRole', ...

    public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
    public ApplicationRole() 
    {
        this.Id = Guid.NewGuid().ToString();
    }

    public ApplicationRole(string name)
        : this()
    {
        this.Name = name;
    }

    // Add any custom Role properties/code here
}


// Must be expressed in terms of our custom types:
public class ApplicationDbContext 
    : IdentityDbContext<ApplicationUser, ApplicationRole, 
    string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }

    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    // Add additional items here as needed
}

Để tiết kiệm thời gian, chúng tôi có thể sử dụng Mẫu dự án mở rộng AspNet 2.0 để mở rộng tất cả các lớp.

Sự lựa chọn thứ hai:(Không được đề xuất)
Chúng tôi thực sự không phải kế thừa từ IdentityDbContext nếu chúng tôi tự viết tất cả mã.
Vì vậy, về cơ bản, chúng ta chỉ có thể kế thừa từ DbContext và triển khai phiên bản tùy chỉnh "OnModelCreating (Trình xây dựng ModelBuilder)" từ mã nguồn IdentityDbContext


2
@ mike-devenney Đây là câu trả lời của bạn về việc hợp nhất hai lớp bối cảnh, hy vọng nó có ích.
Arvand

1
Cảm ơn Arvand, tôi đã bỏ lỡ điều này và thật kỳ lạ khi tình cờ gặp lại nó 1,5 năm sau trong khi nhìn vào chủ đề một lần nữa. :)
Mike Devenney

9

Đây là một mục muộn cho mọi người, nhưng dưới đây là triển khai của tôi. Bạn cũng sẽ nhận thấy tôi đã loại bỏ khả năng thay đổi loại mặc định của KEYs: các chi tiết có thể tìm thấy trong các bài viết sau:

GHI CHÚ:
Cần lưu ý rằng bạn không thể sử dụng Guid'scho các phím của mình. Điều này là do dưới mui xe, chúng là một Struct, và như vậy, không có hộp thư nào cho phép chuyển đổi từ một <TKey>tham số chung .

CÁC LỚP XEM NHƯ:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, string, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
    #region <Constructors>

    public ApplicationDbContext() : base(Settings.ConnectionString.Database.AdministrativeAccess)
    {
    }

    #endregion

    #region <Properties>

    //public DbSet<Case> Case { get; set; }

    #endregion

    #region <Methods>

    #region

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        //modelBuilder.Configurations.Add(new ResourceConfiguration());
        //modelBuilder.Configurations.Add(new OperationsToRolesConfiguration());
    }

    #endregion

    #region

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    #endregion

    #endregion
}

    public class ApplicationUser : IdentityUser<string, CustomUserLogin, CustomUserRole, CustomUserClaim>
    {
        #region <Constructors>

        public ApplicationUser()
        {
            Init();
        }

        #endregion

        #region <Properties>

        [Required]
        [StringLength(250)]
        public string FirstName { get; set; }

        [Required]
        [StringLength(250)]
        public string LastName { get; set; }

        #endregion

        #region <Methods>

        #region private

        private void Init()
        {
            Id = Guid.Empty.ToString();
        }

        #endregion

        #region public

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);

            // Add custom user claims here

            return userIdentity;
        }

        #endregion

        #endregion
    }

    public class CustomUserStore : UserStore<ApplicationUser, CustomRole, string, CustomUserLogin, CustomUserRole, CustomUserClaim>
    {
        #region <Constructors>

        public CustomUserStore(ApplicationDbContext context) : base(context)
        {
        }

        #endregion
    }

    public class CustomUserRole : IdentityUserRole<string>
    {
    }

    public class CustomUserLogin : IdentityUserLogin<string>
    {
    }

    public class CustomUserClaim : IdentityUserClaim<string> 
    { 
    }

    public class CustomRoleStore : RoleStore<CustomRole, string, CustomUserRole>
    {
        #region <Constructors>

        public CustomRoleStore(ApplicationDbContext context) : base(context)
        {
        } 

        #endregion
    }

    public class CustomRole : IdentityRole<string, CustomUserRole>
    {
        #region <Constructors>

        public CustomRole() { }
        public CustomRole(string name) 
        { 
            Name = name; 
        }

        #endregion
    }

8

Nếu bạn đi sâu vào các bản tóm tắt của IdentityDbContext, bạn sẽ thấy rằng nó trông giống như DbContext dẫn xuất của bạn. Con đường dễ nhất là câu trả lời của Olav, nhưng nếu bạn muốn kiểm soát nhiều hơn những gì được tạo ra và ít phụ thuộc hơn vào các gói Nhận dạng, hãy xem câu hỏi và câu trả lời của tôi ở đây . Có một ví dụ mã nếu bạn theo liên kết, nhưng tóm lại, bạn chỉ cần thêm DbSets cần thiết vào lớp con DbContext của riêng bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.