[asp.net] ASP.NET ID DbContext 혼란

기본 MVC 5 앱은 IdentityModels.cs에서이 코드와 함께 제공됩니다.이 코드는 기본 템플릿에 대한 모든 ASP.NET Identity 작업을위한 것입니다.

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

Entity Framework가있는 뷰를 사용하여 새 컨트롤러를 스캐 폴드하고 대화 상자에서 “새 데이터 컨텍스트 …”를 생성하면 다음과 같이 생성됩니다.

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; }

    }
} 

예를 들어 Animal 모델과 같이 EF를 사용하여 다른 컨트롤러 +보기를 스캐 폴딩하면이 새로운 라인 public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }이 다음과 같이 자동 생성됩니다 .

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(모든 ASP.NET ID 항목에 대해)는에서 IdentityDbContext상속받는 상속 DbContext합니다.
AllOtherStuffDbContext(내 물건)은에서 상속받습니다 DbContext.

그래서 내 질문은 :

다른 모든 모델에 사용해야 하는이 두 ( ApplicationDbContextAllOtherStuffDbContext) 중 어느 것 입니까? 아니면 ApplicationDbContext기본 클래스에서 파생되므로 기본 자동 생성 을 사용해야 DbContext합니까? 아니면 오버 헤드가 있습니까? 당신은 하나를 사용합니다 DbContext심지어 모두 사용을 고려해서는 안 모든 모델 (I이 어딘가를 읽을했습니다)에 대한 앱에서 개체를 ApplicationDbContext하고 AllOtherStuffDbContext하나의 응용 프로그램에서? 또는 ASP.NET Identity를 사용하는 MVC 5의 모범 사례는 무엇입니까?



답변

IdentityDbContext에서 상속되는 단일 Context 클래스를 사용합니다. 이렇게하면 클래스와 IdentityUser 및 IdentityDbContext의 역할 사이의 관계를 컨텍스트가 인식하도록 할 수 있습니다. IdentityDbContext에는 오버 헤드가 거의 없으며 기본적으로 두 개의 DbSet이있는 일반 DbContext입니다. 하나는 사용자를위한 것이고 다른 하나는 역할을위한 것입니다.


답변

에 대한 많은 혼란이있다 IdentityDbContext , 당신이이 질문을 찾을 수 있습니다와 유래에 빠른 검색 :

왜 Asp.Net 신원 IdentityDbContext은 블랙 박스?
비주얼 스튜디오 2013 ASPNET ID를 사용하는 경우 어떻게 테이블 이름을 변경할 수 있습니까?
IdentityDbContext와 MyDbContext 병합

이러한 모든 질문에 대답하기 위해 IdentityDbContext 는 DbContext에서 상속 된 클래스 라는 것을 이해해야합니다 . 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");
        });
    }
}

소스 코드를 기반으로 IdentityDbContext를 DbContext와 병합하려는 경우 두 가지 옵션이 있습니다.

첫 번째 옵션 :
IdentityDbContext에서 상속하고 클래스에 액세스 할 수있는 DbContext를 만듭니다.

   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
}

추가 사항 :

1) 다음 솔루션으로 asp.net Identity 기본 테이블 이름을 변경할 수도 있습니다.

    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) 또한 각 클래스를 확장하고 ‘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
}

시간을 절약하기 위해 AspNet Identity 2.0 Extensible Project Template 을 사용하여 모든 클래스를 확장 할 수 있습니다.

두 번째 옵션 :(권장하지 않음)
모든 코드를 직접 작성하는 경우 실제로 IdentityDbContext에서 상속 할 필요는 없습니다.
따라서 기본적으로 DbContext에서 상속하고 IdentityDbContext 소스 코드 에서 “OnModelCreating (ModelBuilder builder)”의 사용자 정의 버전을 구현할 수 있습니다.


답변

이것은 사람들에게 늦은 항목이지만 아래는 내 구현입니다. KEYs 기본 유형을 변경하는 기능을 스텁 아웃했음을 알 수 있습니다. 자세한 내용은 다음 기사에서 확인할 수 있습니다.

참고 :
에는 사용할 수 없습니다 Guid's. 이것은 후드 아래에 있기 때문에 Struct일반 <TKey>매개 변수 에서 변환 할 수있는 unboxing이 없기 때문 입니다.

수업은 다음과 같습니다.

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
    }


답변

IdentityDbContext의 추상화를 자세히 살펴보면 파생 된 DbContext와 유사하다는 것을 알 수 있습니다. 가장 쉬운 경로는 Olav의 답변이지만 생성되는 것을 더 많이 제어하고 Identity 패키지에 대한 의존도를 줄이려면 내 질문과 대답을 살펴보십시오 . 링크를 따라 가면 코드 예제가 있지만 요약하면 필요한 DbSet을 자신의 DbContext 서브 클래스에 추가하기 만하면됩니다.


답변