Confusão do ASP.NET Identity DbContext

Um aplicativo MVC 5 padrão vem com este trecho de código em IdentityModels.cs – este trecho de código é para todas as operações de identidade do ASP.NET para os modelos padrão:

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

Se eu criar um novo controlador usando visualizações com o Entity Framework e criar um “Novo contexto de dados …” no diálogo, gerarei isso para mim:

 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 Movies { get; set; } } } 

Se eu criar outro controller + view usando o EF, por exemplo, para um modelo Animal, essa nova linha será autogerada diretamente em public System.Data.Entity.DbSet Movies { get; set; } public System.Data.Entity.DbSet Movies { get; set; } public System.Data.Entity.DbSet Movies { get; set; } – assim:

 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 Movies { get; set; } public System.Data.Entity.DbSet Animals { get; set; } } } 

ApplicationDbContext (para todo o material de identidade do ASP.NET) herda de IdentityDbContext que por sua vez herda de DbContext . AllOtherStuffDbContext (para minhas próprias coisas) herda do DbContext .

Então minha pergunta é:

Qual desses dois ( ApplicationDbContext e AllOtherStuffDbContext ) devo usar para todos os meus outros modelos? Ou devo usar apenas o ApplicationDbContext gerado automaticamente, já que não deveria ser um problema usá-lo, já que deriva da class base DbContext , ou haverá alguma sobrecarga? Você deve usar apenas um object DbContext em seu aplicativo para todos os seus modelos (eu li isso em algum lugar), então eu não deveria sequer considerar o uso de ApplicationDbContext e AllOtherStuffDbContext em um único aplicativo? Ou qual é a melhor prática no MVC 5 com o ASP.NET Identity?

Eu usaria uma única class Context herdando de IdentityDbContext. Dessa forma, você pode ter o contexto ciente de quaisquer relações entre suas classs e o IdentityUser e as Funções do IdentityDbContext. Há muito pouca sobrecarga no IdentityDbContext, é basicamente um DbContext regular com dois DbSets. Um para os usuários e um para os papéis.

Há muita confusão sobre o IdentityDbContext , uma busca rápida no Stackoverflow e você encontrará estas perguntas:
” Por que IdentityDbContext é uma checkbox preta do Asp.Net Identity?
Como posso alterar os nomes das tabelas ao usar o Visual Studio 2013 AspNet Identity?
Mesclar MyDbContext com IdentityDbContext ”

Para responder a todas essas perguntas, precisamos entender que o IdentityDbContext é apenas uma class herdada do DbContext.
Vamos dar uma olhada na fonte do IdentityDbContext :

 ///  /// Base class for the Entity Framework database context used for identity. ///  /// The type of user objects. /// The type of role objects. /// The type of the primary key for users and roles. /// The type of the user claim object. /// The type of the user role object. /// The type of the user login object. /// The type of the role claim object. /// The type of the user token object. public abstract class IdentityDbContext : DbContext where TUser : IdentityUser where TRole : IdentityRole where TKey : IEquatable where TUserClaim : IdentityUserClaim where TUserRole : IdentityUserRole where TUserLogin : IdentityUserLogin where TRoleClaim : IdentityRoleClaim where TUserToken : IdentityUserToken { ///  /// Initializes a new instance of . ///  /// The options to be used by a . public IdentityDbContext(DbContextOptions options) : base(options) { } ///  /// Initializes a new instance of the  class. ///  protected IdentityDbContext() { } ///  /// Gets or sets the  of Users. ///  public DbSet Users { get; set; } ///  /// Gets or sets the  of User claims. ///  public DbSet UserClaims { get; set; } ///  /// Gets or sets the  of User logins. ///  public DbSet UserLogins { get; set; } ///  /// Gets or sets the  of User roles. ///  public DbSet UserRoles { get; set; } ///  /// Gets or sets the  of User tokens. ///  public DbSet UserTokens { get; set; } ///  /// Gets or sets the  of roles. ///  public DbSet Roles { get; set; } ///  /// Gets or sets the  of role claims. ///  public DbSet RoleClaims { get; set; } ///  /// Configures the schema needed for the identity framework. ///  ///  /// The builder being used to construct the model for this context. ///  protected override void OnModelCreating(ModelBuilder builder) { builder.Entity(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(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(b => { b.HasKey(uc => uc.Id); b.ToTable("AspNetUserClaims"); }); builder.Entity(b => { b.HasKey(rc => rc.Id); b.ToTable("AspNetRoleClaims"); }); builder.Entity(b => { b.HasKey(r => new { r.UserId, r.RoleId }); b.ToTable("AspNetUserRoles"); }); builder.Entity(b => { b.HasKey(l => new { l.LoginProvider, l.ProviderKey }); b.ToTable("AspNetUserLogins"); }); builder.Entity(b => { b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name }); b.ToTable("AspNetUserTokens"); }); } } 

Com base no código-fonte, se quisermos mesclar IdentityDbContext com nosso DbContext, temos duas opções:

Primeira opção:
Crie um DbContext que herda do IdentityDbContext e tenha access às classs.

  public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext() : base("DefaultConnection") { } static ApplicationDbContext() { Database.SetInitializer(new ApplicationDbInitializer()); } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } // Add additional items here as needed } 

Notas Extra:

1) Também podemos alterar os nomes das tabelas padrão do asp.net Identity com a seguinte solução:

  public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(): base("DefaultConnection") { } protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity().ToTable("user"); modelBuilder.Entity().ToTable("user"); modelBuilder.Entity().ToTable("role"); modelBuilder.Entity().ToTable("userrole"); modelBuilder.Entity().ToTable("userclaim"); modelBuilder.Entity().ToTable("userlogin"); } } 

2) Além disso, podemos estender cada class e adicionar qualquer propriedade para classs como ‘IdentityUser’, ‘IdentityRole’, …

  public class ApplicationRole : IdentityRole { 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 { public ApplicationDbContext() : base("DefaultConnection") { } static ApplicationDbContext() { Database.SetInitializer(new ApplicationDbInitializer()); } public static ApplicationDbContext Create() { return new ApplicationDbContext(); } // Add additional items here as needed } 

Para economizar tempo, podemos usar o Modelo de Projeto Extensível AspNet Identity 2.0 para estender todas as classs.

Segunda opção: (não recomendado)
Na verdade, não precisamos herdar IdentityDbContext se escrevermos todo o código por nós mesmos.
Então, basicamente, podemos herdar do DbContext e implementar nossa versão personalizada de “OnModelCreating (ModelBuilder builder)” do código-fonte do IdentityDbContext

Se você detalhar as abstrações do IdentityDbContext, verá que ele se parece com o DbContext derivado. O caminho mais fácil é a resposta do Olav, mas se você quiser mais controle sobre o que está sendo criado e um pouco menos de dependência nos pacotes de Identidade, dê uma olhada na minha pergunta e resposta aqui . Há um exemplo de código se você seguir o link, mas em resumo você acabou de adicionar os DbSets necessários à sua própria subclass DbContext.

Esta é uma input tardia para pessoas, mas abaixo está minha implementação. Você também notará que consegui alterar o tipo padrão de KEYs: os detalhes sobre os quais podem ser encontrados nos seguintes artigos:

  • Estendendo modelos de identidade e usando chaves inteiras em vez de seqüências de caracteres
  • Alterar chave primária para usuários na identidade do ASP.NET

NOTAS:
Deve-se notar que você não pode usar o Guid's para suas chaves. Isso ocorre porque, sob o capô, eles são um Struct e, como tal, não têm unboxing que permita sua conversão de um parâmetro genérico.

AS CLASSES OLHAM COMO:

 public class ApplicationDbContext : IdentityDbContext { #region  public ApplicationDbContext() : base(Settings.ConnectionString.Database.AdministrativeAccess) { } #endregion #region  //public DbSet Case { get; set; } #endregion #region  #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 { #region  public ApplicationUser() { Init(); } #endregion #region  [Required] [StringLength(250)] public string FirstName { get; set; } [Required] [StringLength(250)] public string LastName { get; set; } #endregion #region  #region private private void Init() { Id = Guid.Empty.ToString(); } #endregion #region public public async Task GenerateUserIdentityAsync(UserManager 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 { #region  public CustomUserStore(ApplicationDbContext context) : base(context) { } #endregion } public class CustomUserRole : IdentityUserRole { } public class CustomUserLogin : IdentityUserLogin { } public class CustomUserClaim : IdentityUserClaim { } public class CustomRoleStore : RoleStore { #region  public CustomRoleStore(ApplicationDbContext context) : base(context) { } #endregion } public class CustomRole : IdentityRole { #region  public CustomRole() { } public CustomRole(string name) { Name = name; } #endregion }