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

ASP.NET Core 3.1 EF Core:无法移除NormalizedName唯一索引,需创建联合索引

解决ASP.NET Core 3.1中IdentityRole联合唯一索引替换默认NormalizedName唯一索引的问题

问题原因

ASP.NET Identity框架默认会为IdentityRole<TKey>的NormalizedName属性创建唯一索引,仅通过builder.HasIndex(c => c.NormalizedName).IsUnique(false)无法覆盖或移除这个默认索引,导致迁移仅生成新的联合索引,未修改原有唯一约束。

解决方案

1. 移除默认索引并重新配置

在ApplicationRole的实体配置中,先移除Identity默认创建的NormalizedName唯一索引,再按需添加非唯一索引和联合唯一索引:

public void Configure(EntityTypeBuilder<ApplicationRole> builder)
{
    // 移除Identity默认的NormalizedName唯一索引
    var normalizedNameIndex = builder.Metadata.FindIndex(nameof(ApplicationRole.NormalizedName));
    if (normalizedNameIndex != null)
    {
        builder.Metadata.RemoveIndex(normalizedNameIndex);
    }

    // 可选:保留NormalizedName的非唯一索引(若无需单独索引可删除此行)
    builder.HasIndex(c => c.NormalizedName).IsUnique(false);

    // 添加多属性联合唯一索引
    builder.HasIndex(c => new { c.NormalizedName, c.SupplierId, c.CustomerId }).IsUnique(true);
}

2. 生成迁移并更新数据库

执行以下命令生成包含索引修改的迁移脚本:

Add-Migration UpdateRoleUniqueIndex

随后更新数据库:

Update-Database

3. 同步应用层验证逻辑

由于默认的RoleValidator会验证NormalizedName的唯一性,需确保自定义的TenantRoleValidator验证联合属性的唯一性,避免应用层与数据库层约束不一致:

public class TenantRoleValidator : RoleValidator<ApplicationRole>
{
    private readonly ApplicationDbContext _context;

    public TenantRoleValidator(ApplicationDbContext context, IdentityErrorDescriber errors = null) 
        : base(errors)
    {
        _context = context;
    }

    public override async Task<IdentityResult> ValidateAsync(RoleManager<ApplicationRole> manager, ApplicationRole role)
    {
        var result = await base.ValidateAsync(manager, role);

        // 验证NormalizedName+SupplierId+CustomerId的组合唯一性
        var existingRole = await _context.Roles
            .FirstOrDefaultAsync(r => r.NormalizedName == role.NormalizedName 
                && r.SupplierId == role.SupplierId 
                && r.CustomerId == role.CustomerId 
                && r.Id != role.Id);

        if (existingRole != null)
        {
            result = IdentityResult.Failed(new IdentityError
            {
                Code = "DuplicateRoleCombination",
                Description = "同一NormalizedName、SupplierId和CustomerId组合的角色已存在。"
            });
        }

        return result;
    }
}

内容的提问来源于stack exchange,提问作者Tariq Hajeer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 00:07:48