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

EF Core 6中如何播种带规范化字段的Identity角色与用户数据?

解决Identity角色/用户播种时规范化属性(NormalizedName等)为null的问题

在使用Microsoft.AspNetCore.Identity.EntityFrameworkCore v6.0.8和Microsoft.EntityFrameworkCore v6.0.8播种Identity角色与用户时,即使手动指定了NormalizedName、NormalizedUserName等规范化属性的值,数据库中这些字段始终为null。现有代码如下:

public class AppDbContext : IdentityDbContext<User, Role, int>
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<Role>().HasData(new Role
        {
            Id = 1,
            Name = "SystemAdmin",
            NormalizedName = "SYSTEMADMIN"
        });

        base.OnModelCreating(builder);
    }
}

问题原因

Identity的base.OnModelCreating(builder)方法会自动为NormalizedName、NormalizedUserName、NormalizedEmail等规范化属性配置值生成策略(ValueGeneratedOnAddOrUpdate),这会让EF Core认为这些值由系统自动生成,从而忽略你在HasData中手动指定的固定值。

解决方案

方案1:调整代码顺序(推荐)

将base.OnModelCreating(builder)的调用移至种子数据配置之前,让Identity完成默认配置后,再通过HasData覆盖规范化属性的固定值:

public class AppDbContext : IdentityDbContext<User, Role, int>
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        // 先执行Identity的默认配置
        base.OnModelCreating(builder);

        // 配置角色种子数据
        builder.Entity<Role>().HasData(new Role
        {
            Id = 1,
            Name = "SystemAdmin",
            NormalizedName = "SYSTEMADMIN",
            ConcurrencyStamp = Guid.NewGuid().ToString() // 需指定并发戳,避免Identity验证报错
        });

        // 配置用户种子数据示例
        builder.Entity<User>().HasData(new User
        {
            Id = 1,
            UserName = "admin@example.com",
            NormalizedUserName = "ADMIN@EXAMPLE.COM",
            Email = "admin@example.com",
            NormalizedEmail = "ADMIN@EXAMPLE.COM",
            EmailConfirmed = true,
            PasswordHash = GetHashedPassword(), // 使用加密后的密码
            SecurityStamp = Guid.NewGuid().ToString(),
            ConcurrencyStamp = Guid.NewGuid().ToString()
        });
    }

    // 生成加密密码的辅助方法
    private string GetHashedPassword()
    {
        var hasher = new PasswordHasher<User>();
        return hasher.HashPassword(null, "YourSecurePassword123!");
    }
}

注意:

  • 必须为角色和用户指定ConcurrencyStamp(可用Guid.NewGuid().ToString()生成),否则Identity会因为并发戳验证失败拒绝数据。
  • 用户的PasswordHash不能直接写明文,必须使用PasswordHasher生成加密后的哈希值。

方案2:手动移除值生成策略

如果无法调整代码顺序,可以手动移除规范化属性的自动生成配置,让EF Core使用你指定的固定值:

public class AppDbContext : IdentityDbContext<User, Role, int>
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<Role>().HasData(new Role
        {
            Id = 1,
            Name = "SystemAdmin",
            NormalizedName = "SYSTEMADMIN",
            ConcurrencyStamp = Guid.NewGuid().ToString()
        });

        base.OnModelCreating(builder);

        // 移除角色NormalizedName的自动生成配置
        builder.Entity<Role>()
            .Property(r => r.NormalizedName)
            .ValueGeneratedNever();

        // 若需要配置用户,同样移除对应属性的自动生成
        builder.Entity<User>()
            .Property(u => u.NormalizedUserName)
            .ValueGeneratedNever();
        builder.Entity<User>()
            .Property(u => u.NormalizedEmail)
            .ValueGeneratedNever();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 12:57:16