如何在.NET 6 Identity中添加导航属性且不生成Discriminator列
问题背景
我在使用.NET 6开发API项目,采用Identity组件。初始定义的User和Role类如下:
public class ApplicationUser : IdentityUser<int> { public virtual ICollection<IdentityUserRole<int>> Roles { get; } = new List<IdentityUserRole<int>>(); } public class ApplicationRole : IdentityRole<int> { public virtual ICollection<IdentityUserRole<int>> Users { get; } = new List<IdentityUserRole<int>>(); }
对应的生成表结构(AspNetUserRoles)如下:
migrationBuilder.CreateTable( name: "AspNetUserRoles", schema: "ancid", columns: table => new { UserId = table.Column<int>(type: "int", nullable: false), RoleId = table.Column<int>(type: "int", nullable: false) }, constraints: table => { //... });
此时从ApplicationUser只能导航到IdentityUserRole<int>获取RoleId,无法直接获取角色名称。于是我自定义了ApplicationUserRole并添加导航属性,修改后的类:
public class ApplicationUserRole : IdentityUserRole<int> { public virtual ApplicationUser User { get; } public virtual ApplicationRole Role { get; } } public class ApplicationUser : IdentityUser<int> { public virtual ICollection<ApplicationUserRole> Roles { get; } = new List<ApplicationUserRole>(); } public class ApplicationRole : IdentityRole<int> { public virtual ICollection<ApplicationUserRole> Users { get; } = new List<ApplicationUserRole>(); }
但生成的迁移会自动为AspNetUserRoles表添加Discriminator列:
protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AddColumn<string>( name: "Discriminator", schema: "ancid", table: "AspNetUserRoles", type: "nvarchar(max)", nullable: false, defaultValue: ""); }
即使删除迁移目录重新生成初始迁移,该列依然存在,而AspNetUsers和AspNetRoles表没有这个问题。需要实现添加导航属性但不生成Discriminator列的目标。
解决方案
1. 在DbContext中显式配置实体映射
在继承自IdentityDbContext的DbContext类中,重写OnModelCreating方法,显式指定ApplicationUserRole对应的表,并告知EF Core这是该表的唯一实体类型,不需要鉴别器:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 配置ApplicationUserRole映射到AspNetUserRoles表,关闭鉴别器 modelBuilder.Entity<ApplicationUserRole>(b => { b.HasKey(r => new { r.UserId, r.RoleId }); b.ToTable("AspNetUserRoles", "ancid"); // 指定表名和schema,和之前一致 // 显式配置导航属性关联 b.HasOne(r => r.User) .WithMany(u => u.Roles) .HasForeignKey(r => r.UserId) .IsRequired(); b.HasOne(r => r.Role) .WithMany(r => r.Users) .HasForeignKey(r => r.RoleId) .IsRequired(); }); }
2. 确保IdentityDbContext使用自定义的UserRole类型
在DbContext的继承声明中,明确指定自定义的ApplicationUserRole作为泛型参数,让EF Core默认使用你的自定义类型而非基类IdentityUserRole<int>:
public class YourDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, IdentityUserClaim<int>, ApplicationUserRole, IdentityUserLogin<int>, IdentityRoleClaim<int>, IdentityUserToken<int>> { public YourDbContext(DbContextOptions<YourDbContext> options) : base(options) { } }
3. 重新生成迁移
删除现有的迁移文件和开发环境数据库,执行以下命令重新生成初始迁移并更新数据库:
dotnet ef migrations add InitialCreate dotnet ef database update
此时生成的迁移中不会再出现Discriminator列,同时ApplicationUser和ApplicationRole可以通过ApplicationUserRole的导航属性直接关联获取相关数据。
内容的提问来源于stack exchange,提问作者tschmit

