ASP.NET ID DbContext 혼란


196

기본 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의 모범 사례는 무엇입니까?


1
그건 그렇고; 문서를 스캔하는 동안 이것은 눈에 띄지 않으며 필요하지 않습니다. public System.Data.Entity.DbSet <WebApplication1.Models.Movie> Movies {get; 세트; }-System.Data.Entity 및 WebApplication1.Models 부분 선언에서 제거하고 대신 using 문 섹션에 네임 스페이스를 추가 할 수 없습니까?
PussInBoots

고름-귀하의 의견에 예. 잘 작동합니다.
SB2055

이것은 MongoDB.Driver (> = v2.1.0
Stanislav Prusac

답변:


178

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


52
이는 단일 MVC5 프로젝트를위한 것이지만 파생 된 DbContext가 MVC5가 아닌 여러 프로젝트간에 공유 될 때는 바람직하지 않으며 일부는 Identity 지원이 필요하지 않습니다.
Dave

더 쉬운 유지 보수성과 더 나은 관계 무결성을 위해 동일한 데이터베이스에 투표했습니다. 사용자 엔터티와 역할 엔터티는 다른 응용 프로그램 개체와 쉽게 관련되기 때문입니다.
IBMer

6
@Dave-두 개의 다른 컨텍스트를 사용하여 사용자 데이터의 파티션을 복잡하게합니다. MVC 앱은 사용자별로 데이터를 분할하지만 다른 앱은 분할하지 않습니다. 동일한 데이터 계층을 공유하는 것이 일반적이지만, 일부 프로젝트는 사용자가 데이터를 분할해야하지만 일부는 그렇지 않은 것이 일반적이라고 생각하지 않습니다.
RickAndMSFT

1
누구든지 MVC 프로젝트에서 ApplicationDBContext를 추출하여 기존 EF 데이터 계층에 포함시키는 방법을 알고 있습니까? 위에서 설명한 것처럼 두 가지를 병합하는 것이 올바른 접근 방법 인 것처럼 보이지만 시간이 제한된 프로젝트를 진행하고 있습니다. 나는 그것을 처음부터 제대로하고 싶지만 내 앞에있는 모든 문제에 대해 머리를 좋아할 것입니다 ...
Mike Devenney

7
약 1 시간 동안이 답변을 찾은 후 올바른 방향으로 나에게 지적했지만 그것을 구현하는 방법을 모르겠습니다 (매우 문자 그대로의 사람). 다른 사람에게 도움이된다면 가장 간단한 방법은 IdentityModels.cs를 열고 ApplicationDbContext 클래스에 새 DbSet을 추가하는 것입니다.
SeanOB

45

에 대한 많은 혼란이있다 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)"의 사용자 정의 버전을 구현할 수 있습니다.


2
@ mike-devenney 여기 두 컨텍스트 레이어를 병합하는 방법에 대한 답변을 드리겠습니다.
Arvand

1
감사합니다 Arvand, 나는 이것을 놓 쳤고 1.5 년 후에 주제를 다시 살펴보면서 이상하게 넘어졌습니다. :)
마이크 Devenney

9

이것은 사람들에게 늦은 항목이지만 아래는 내 구현입니다. 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
    }

8

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.