You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Identity+EF6添加用户到角色时重复创建用户和角色问题

Blazor Identity 用户添加角色异常问题解决

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 07:07:54