ASP.NET Identity+EF6添加用户到角色时重复创建用户和角色问题
ApplicationDbContext 代码
public class ApplicationDbContext : IdentityDbContext<AppUser, AppRole, string, AppUserClaim,AppUserRole,AppUserLogin,AppRoleClaim,AppUserToken> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity<AppUser>().ToTable("Users"); builder.Entity<AppUser>().Property(x => x.FirstName).IsRequired(); builder.Entity<AppUser>().Property(x => x.LastName).IsRequired(); builder.Entity<AppUser>().Property(x => x.LastLoginDate); builder.Entity<AppUser>().HasMany(x => x.Roles).WithOne(x=>x.User).HasForeignKey(x=>x.UserId).IsRequired(); builder.Entity<AppUserRole>().ToTable("UserRoles").HasOne(c => c.Role).WithMany(c => c.UserRoles).HasForeignKey(c => c.UserId); builder.Entity<AppRole>().ToTable("Roles"); builder.Entity<AppRole>().Property(x => x.Description); builder.Entity<AppRole>().HasMany(x => x.UserRoles).WithOne(e => e.Role).HasForeignKey(ur => ur.RoleId).IsRequired(); builder.Entity<AppRole>().HasMany(x => x.RoleClaims).WithOne(e=>e.Role).HasForeignKey(x=>x.RoleId).IsRequired(); builder.Entity<AppRoleClaim>().ToTable("RoleClaims"); builder.Entity<AppRoleClaim>().HasOne(x => x.Role); builder.Entity<AppUserClaim>().ToTable("UserClaims"); } }
Program.cs 代码
builder.Services.AddIdentity<AppUser, AppRole>() .AddEntityFrameworkStores<ApplicationDbContext>(); builder.Services.AddTransient<RoleManager<AppRole>>();
自定义Identity类
public class AppRole:IdentityRole { public string? Description { get; set; } public virtual ICollection<AppUserRole>? UserRoles { get; set; } public virtual ICollection<AppRoleClaim>? RoleClaims { get; set; } public AppRole() { } } public class AppRoleClaim : IdentityRoleClaim<string> { public virtual AppRole Role { get; set; } public AppRoleClaim() { Role = new AppRole(); } } public class AppUser : IdentityUser { public string FirstName { get; set; } public string LastName { get; set; } public DateTimeOffset? LastLoginDate { get; set; } public byte[]? Photo { get; set; } public virtual ICollection<AppUserRole> Roles { get; set; } public AppUser() { FirstName = ""; LastName = ""; Roles = new List<AppUserRole>(); } } public class AppUserClaim : IdentityUserClaim<string> { } public class AppUserLogin : IdentityUserLogin<string> { } public class AppUserRole : IdentityUserRole<string> { public virtual AppUser User { get; set; } public virtual AppRole Role { get; set; } public AppUserRole() { User = new AppUser(); Role = new AppRole(); } } public class AppUserToken : IdentityUserToken<string> { }
表结构

问题详情
调用UserManager.CreateAsync(NewUser)可正常创建用户,但执行await UserManager.AddToRoleAsync(NewUser, "Admin");时,无任何报错,却在数据库中创建了一个用户名、姓名等字段为空的新用户,同时创建了名称和规范化名称均为空的新角色,并将该空角色关联到UserRoles表。按预期此操作应仅将已创建用户添加到Admin角色,而非创建新的空用户和角色。
问题原因及修复方案
1. 导航属性构造函数实例化导致空实体创建
AppUserRole和AppRoleClaim的构造函数中,手动实例化了关联的AppUser、AppRole对象。EF Core处理导航属性时会自动跟踪关联实体,手动创建的新实例会被误认为需要插入的新实体,从而生成空的用户和角色记录。
修复:
移除构造函数中对导航属性的实例化:
// 修改AppRoleClaim public class AppRoleClaim : IdentityRoleClaim<string> { public virtual AppRole Role { get; set; } public AppRoleClaim() { // 移除 Role = new AppRole(); } } // 修改AppUserRole public class AppUserRole : IdentityUserRole<string> { public virtual AppUser User { get; set; } public virtual AppRole Role { get; set; } public AppUserRole() { // 移除 User = new AppUser(); // 移除 Role = new AppRole(); } }
2. AppUserRole外键配置错误
ApplicationDbContext的OnModelCreating方法中,配置AppUserRole的Role外键时错误指定了UserId,正确外键应为RoleId。
修复:
修改AppUserRole的配置代码:
builder.Entity<AppUserRole>() .ToTable("UserRoles") .HasOne(c => c.Role) .WithMany(c => c.UserRoles) .HasForeignKey(c => c.RoleId); // 将UserId改为RoleId
3. 手动注册RoleManager导致服务冲突
AddIdentity<AppUser, AppRole>()已自动注册RoleManager<AppRole>,手动添加builder.Services.AddTransient<RoleManager<AppRole>>();会导致服务注册重复,引发异常行为。
修复:
移除Program.cs中手动注册RoleManager的代码:
// 移除该行 // builder.Services.AddTransient<RoleManager<AppRole>>();
4. 验证Admin角色存在性
调用AddToRoleAsync前,确保"Admin"角色已存在于数据库中。若不存在,需先通过RoleManager.CreateAsync(new AppRole { Name = "Admin" })创建角色。
内容的提问来源于stack exchange,提问作者ksliman

