ASP.NET Identity DbContext confusion ASP.NET Identity DbContext confusion asp.net asp.net

ASP.NET Identity DbContext confusion


I would use a single Context class inheriting from IdentityDbContext.This way you can have the context be aware of any relations between your classes and the IdentityUser and Roles of the IdentityDbContext.There is very little overhead in the IdentityDbContext, it is basically a regular DbContext with two DbSets. One for the users and one for the roles.


There is a lot of confusion about IdentityDbContext, a quick search in Stackoverflow and you'll find these questions:
"Why is Asp.Net Identity IdentityDbContext a Black-Box?
How can I change the table names when using Visual Studio 2013 AspNet Identity?
Merge MyDbContext with IdentityDbContext"

To answer to all of these questions we need to understand that IdentityDbContext is just a class inherited from DbContext.
Let's take a look at IdentityDbContext source:

/// <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");        });    }}


Based on the source code if we want to merge IdentityDbContext with our DbContext we have two options:

First Option:
Create a DbContext which inherits from IdentityDbContext and have access to the classes.

   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}


Extra Notes:

1) We can also change asp.net Identity default table names with the following solution:

    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) Furthermore we can extend each class and add any property to classes like '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}

To save time we can use AspNet Identity 2.0 Extensible Project Template to extend all the classes.

Second Option:(Not recommended)
We actually don't have to inherit from IdentityDbContext if we write all the code ourselves.
So basically we can just inherit from DbContext and implement our customized version of "OnModelCreating(ModelBuilder builder)" from the IdentityDbContext source code


This is a late entry for folks, but below is my implementation. You will also notice I stubbed-out the ability to change the the KEYs default type: the details about which can be found in the following articles:

NOTES:
It should be noted that you cannot use Guid's for your keys. This is because under the hood they are a Struct, and as such, have no unboxing which would allow their conversion from a generic <TKey> parameter.

THE CLASSES LOOK LIKE:

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    }