Confusione di DbContext di identity framework di ASP.NET

Un’app MVC 5 predefinita viene fornita con questa parte di codice in IdentityModels.cs – questa parte di codice è valida per tutte le operazioni di Id quadro ASP.NET per i modelli predefiniti:

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

Se impaccio un nuovo controller utilizzando le viste con Entity Framework e creo un “Nuovo contesto dati …” nella finestra di dialogo, viene generato per me:

 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 ho impalcato un altro controller + vista usando EF, ad esempio per un modello Animal, questa nuova linea verrebbe automaticamente generata in public System.Data.Entity.DbSet Movies { get; set; } public System.Data.Entity.DbSet Movies { get; set; } public System.Data.Entity.DbSet Movies { get; set; } – come questo:

 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 (per tutti gli oggetti Identity ASP.NET) eredita da IdentityDbContext che a sua volta eredita da DbContext . AllOtherStuffDbContext (per le mie cose personali) eredita da DbContext .

Quindi la mia domanda è:

Quale di questi due ( ApplicationDbContext e AllOtherStuffDbContext ) dovrei usare per tutti i miei altri modelli? O dovrei semplicemente usare il predefinito ApplicationDbContext generato automaticamente poiché non dovrebbe essere un problema usarlo poiché deriva dalla class base DbContext , o ci sarà qualche overhead? Dovresti utilizzare un solo object DbContext nella tua app per tutti i tuoi modelli (l’ho letto da qualche parte) quindi non dovrei nemmeno prendere in considerazione l’utilizzo di ApplicationDbContext e AllOtherStuffDbContext in una singola app? O qual è la migliore pratica in MVC 5 con ASP.NET Identity?

Vorrei utilizzare una singola class Context ereditata da IdentityDbContext. In questo modo è ansible fare in modo che il contesto sia a conoscenza di eventuali relazioni tra le classi e IdentityUser e Roles di IdentityDbContext. C’è poco overhead in IdentityDbContext, è fondamentalmente un normale DbContext con due DbSet. Uno per gli utenti e uno per i ruoli.

C’è molta confusione su IdentityDbContext , una rapida ricerca in Stackoverflow e troverai queste domande:
” Perché Asp.Net Identity IdentityDbContext a Black-Box?
Come posso modificare i nomi delle tabelle quando si utilizza Visual Studio 2013 AspNet Identity?
Unisci MyDbContext con IdentityDbContext ”

Per rispondere a tutte queste domande, dobbiamo solo capire che IdentityDbContext è solo una class ereditata da DbContext.
Diamo un’occhiata all’origine 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"); }); } } 

In base al codice sorgente se vogliamo unire IdentityDbContext con il nostro DbContext abbiamo due opzioni:

Prima opzione:
Creare un DbContext che erediti da IdentityDbContext e abbia accesso alle classi.

  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 } 

Note extra:

1) Possiamo anche cambiare i nomi delle tabelle di default di asp.net Identity con la seguente soluzione:

  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) Inoltre possiamo estendere ogni class e aggiungere qualsiasi proprietà a classi come ‘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 } 

Per risparmiare tempo, possiamo utilizzare il modello di progetto estensibile AspNet Identity 2.0 per estendere tutte le classi.

Seconda opzione: (non consigliato)
In realtà non dobbiamo ereditare da IdentityDbContext se scriviamo noi stessi tutto il codice.
Quindi, in pratica, possiamo ereditare da DbContext e implementare la nostra versione personalizzata di “OnModelCreating (Builder ModelBuilder)” dal codice sorgente IdentityDbContext

Se analizzi in dettaglio le astrazioni di IdentityDbContext, scoprirai che sembra proprio come il tuo DbContext derivato. La via più semplice è la risposta di Olav, ma se vuoi avere più controllo su ciò che viene creato e un po ‘meno dipendenza dai pacchetti Identity dai un’occhiata alla mia domanda e rispondi qui . Esiste un esempio di codice se segui il collegamento, ma in sintesi aggiungi semplicemente i DbSet richiesti alla sottoclass DbContext.

Questa è una voce in ritardo per la gente, ma qui di seguito è la mia implementazione. Noterai anche che ho cancellato la possibilità di cambiare il tipo di default dei KEY: i dettagli su quali possono essere trovati nei seguenti articoli:

  • Estensione di modelli di identity framework e utilizzo di chiavi intere anziché di stringhe
  • Cambia chiave primaria per gli utenti nell’id quadro ASP.NET

GLI APPUNTI:
Va notato che non è ansible utilizzare Guid's per le proprie chiavi. Questo perché sotto la cappa sono una Struct e, come tali, non hanno un unboxing che permetterebbe la loro conversione da un parametro generico .

LE CLASSI GUARDANO:

 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 }