Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Common; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Data.Entity.Infrastructure.Annotations; using System.Data.Entity.Validation; using System.Data.SqlClient; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; namespace Microsoft.AspNet.Identity.EntityFramework { /// <summary> /// Default db context that uses the default entity types /// </summary> public class IdentityDbContext : IdentityDbContext<IdentityUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> { /// <summary> /// Default constructor which uses the DefaultConnection /// </summary> public IdentityDbContext() : this("DefaultConnection") { } /// <summary> /// Constructor which takes the connection string to use /// </summary> /// <param name="nameOrConnectionString"></param> public IdentityDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { } /// <summary> /// Constructs a new context instance using the existing connection to connect to a database, and initializes it from /// the given model. The connection will not be disposed when the context is disposed if contextOwnsConnection is /// false. /// </summary> /// <param name="existingConnection">An existing connection to use for the new context.</param> /// <param name="model">The model that will back this context.</param> /// <param name="contextOwnsConnection"> /// Constructs a new context instance using the existing connection to connect to a /// database, and initializes it from the given model. The connection will not be disposed when the context is /// disposed if contextOwnsConnection is false. /// </param> public IdentityDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) : base(existingConnection, model, contextOwnsConnection) { } } /// <summary> /// DbContext which uses a custom user entity with a string primary key /// </summary> /// <typeparam name="TUser"></typeparam> public class IdentityDbContext<TUser> : IdentityDbContext<TUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> where TUser : IdentityUser { /// <summary> /// Default constructor which uses the DefaultConnection /// </summary> public IdentityDbContext() : this("DefaultConnection") { } /// <summary> /// Constructor which takes the connection string to use /// </summary> /// <param name="nameOrConnectionString"></param> public IdentityDbContext(string nameOrConnectionString) : this(nameOrConnectionString, true) { } /// <summary> /// Constructor which takes the connection string to use /// </summary> /// <param name="nameOrConnectionString"></param> /// <param name="throwIfV1Schema">Will throw an exception if the schema matches that of Identity 1.0.0</param> public IdentityDbContext(string nameOrConnectionString, bool throwIfV1Schema) : base(nameOrConnectionString) { if (throwIfV1Schema && IsIdentityV1Schema(this)) { throw new InvalidOperationException(IdentityResources.IdentityV1SchemaError); } } /// <summary> /// Constructs a new context instance using the existing connection to connect to a database, and initializes it from /// the given model. The connection will not be disposed when the context is disposed if contextOwnsConnection is /// false. /// </summary> /// <param name="existingConnection">An existing connection to use for the new context.</param> /// <param name="model">The model that will back this context.</param> /// <param name="contextOwnsConnection"> /// Constructs a new context instance using the existing connection to connect to a /// database, and initializes it from the given model. The connection will not be disposed when the context is /// disposed if contextOwnsConnection is false. /// </param> public IdentityDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) : base(existingConnection, model, contextOwnsConnection) { } internal static bool IsIdentityV1Schema(DbContext db) { var originalConnection = db.Database.Connection as SqlConnection; // Give up and assume its ok if its not a sql connection if (originalConnection == null) { return false; } if (db.Database.Exists()) { using (var tempConnection = new SqlConnection(originalConnection.ConnectionString)) { tempConnection.Open(); return VerifyColumns(tempConnection, "AspNetUsers", "Id", "UserName", "PasswordHash", "SecurityStamp", "Discriminator") && VerifyColumns(tempConnection, "AspNetRoles", "Id", "Name") && VerifyColumns(tempConnection, "AspNetUserRoles", "UserId", "RoleId") && VerifyColumns(tempConnection, "AspNetUserClaims", "Id", "ClaimType", "ClaimValue", "User_Id") && VerifyColumns(tempConnection, "AspNetUserLogins", "UserId", "ProviderKey", "LoginProvider"); } } return false; } [SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "Reviewed")] internal static bool VerifyColumns(SqlConnection conn, string table, params string[] columns) { var tableColumns = new List<string>(); using ( var command = new SqlCommand("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME=@Table", conn)) { command.Parameters.Add(new SqlParameter("Table", table)); using (var reader = command.ExecuteReader()) { while (reader.Read()) { // Add all the columns from the table tableColumns.Add(reader.GetString(0)); } } } // Make sure that we find all the expected columns return columns.All(tableColumns.Contains); } } /// <summary> /// IdentityDbContext of IdentityUsers /// </summary> /// <typeparam name="TUser"></typeparam> /// <typeparam name="TRole"></typeparam> /// <typeparam name="TKey"></typeparam> /// <typeparam name="TUserLogin"></typeparam> /// <typeparam name="TUserRole"></typeparam> /// <typeparam name="TUserClaim"></typeparam> public class IdentityDbContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> : DbContext where TUser : IdentityUser<TKey, TUserLogin, TUserRole, TUserClaim> where TRole : IdentityRole<TKey, TUserRole> where TUserLogin : IdentityUserLogin<TKey> where TUserRole : IdentityUserRole<TKey> where TUserClaim : IdentityUserClaim<TKey> { /// <summary> /// Default constructor which uses the "DefaultConnection" connectionString /// </summary> public IdentityDbContext() : this("DefaultConnection") { } /// <summary> /// Constructor which takes the connection string to use /// </summary> /// <param name="nameOrConnectionString"></param> public IdentityDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { } /// <summary> /// Constructs a new context instance using the existing connection to connect to a database, and initializes it from /// the given model. The connection will not be disposed when the context is disposed if contextOwnsConnection is /// false. /// </summary> /// <param name="existingConnection">An existing connection to use for the new context.</param> /// <param name="model">The model that will back this context.</param> /// <param name="contextOwnsConnection"> /// Constructs a new context instance using the existing connection to connect to a /// database, and initializes it from the given model. The connection will not be disposed when the context is /// disposed if contextOwnsConnection is false. /// </param> public IdentityDbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection) : base(existingConnection, model, contextOwnsConnection) { } /// <summary> /// EntitySet of Users /// </summary> public virtual IDbSet<TUser> Users { get; set; } /// <summary> /// EntitySet of Roles /// </summary> public virtual IDbSet<TRole> Roles { get; set; } /// <summary> /// If true validates that emails are unique /// </summary> public bool RequireUniqueEmail { get; set; } /// <summary> /// Maps table names, and sets up relationships between the various user entities /// </summary> /// <param name="modelBuilder"></param> protected override void OnModelCreating(DbModelBuilder modelBuilder) { if (modelBuilder == null) { throw new ArgumentNullException("modelBuilder"); } // Needed to ensure subclasses share the same table var user = modelBuilder.Entity<TUser>() .ToTable("AspNetUsers"); user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId); user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId); user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId); user.Property(u => u.UserName) .IsRequired() .HasMaxLength(256) .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("UserNameIndex") { IsUnique = true })); // CONSIDER: u.Email is Required if set on options? user.Property(u => u.Email).HasMaxLength(256); modelBuilder.Entity<TUserRole>() .HasKey(r => new { r.UserId, r.RoleId }) .ToTable("AspNetUserRoles"); modelBuilder.Entity<TUserLogin>() .HasKey(l => new { l.LoginProvider, l.ProviderKey, l.UserId }) .ToTable("AspNetUserLogins"); modelBuilder.Entity<TUserClaim>() .ToTable("AspNetUserClaims"); var role = modelBuilder.Entity<TRole>() .ToTable("AspNetRoles"); role.Property(r => r.Name) .IsRequired() .HasMaxLength(256) .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("RoleNameIndex") { IsUnique = true })); role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId); } /// <summary> /// Validates that UserNames are unique and case insenstive /// </summary> /// <param name="entityEntry"></param> /// <param name="items"></param> /// <returns></returns> protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) { if (entityEntry != null && entityEntry.State == EntityState.Added) { var errors = new List<DbValidationError>(); var user = entityEntry.Entity as TUser; //check for uniqueness of user name and email if (user != null) { if (Users.Any(u => String.Equals(u.UserName, user.UserName))) { errors.Add(new DbValidationError("User", String.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateUserName, user.UserName))); } if (RequireUniqueEmail && Users.Any(u => String.Equals(u.Email, user.Email))) { errors.Add(new DbValidationError("User", String.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateEmail, user.Email))); } } else { var role = entityEntry.Entity as TRole; //check for uniqueness of role name if (role != null && Roles.Any(r => String.Equals(r.Name, role.Name))) { errors.Add(new DbValidationError("Role", String.Format(CultureInfo.CurrentCulture, IdentityResources.RoleAlreadyExists, role.Name))); } } if (errors.Any()) { return new DbEntityValidationResult(entityEntry, errors); } } return base.ValidateEntity(entityEntry, items); } } }
上面的代码出自:
https://www.symbolsource.org/MyGet/Metadata/aspnetwebstacknightly/Project/Microsoft.AspNet.Identity.EntityFramework/2.0.0-rtm-140221/Release/Default/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs?ImageName=Microsoft.AspNet.Identity.EntityFramework
想查看Microsoft.AspNet.Identity.EntityFramework.dll中IdentityDbContext的源码,却发现Github上Identity的代码和实际调用的dll里面的结构不一致。
找到如上代码,留文备用。