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

使用IEntityTypeConfiguration<ApplicationUser>初始化用户种子数据异常

问题描述

我需要向系统插入若干种子用户,于是编写了ApplicationUserConfiguration类,并在ApplicationDbContext的OnModelCreating方法中注册了这个类。种子值能正常生效,但存在两个问题:

  • 每次执行Add-Migration生成新迁移时,即便用户已经在首次迁移时插入数据库,迁移的Up和Down方法里还是会包含这些用户信息;
  • 修改用户密码后能正常使用,但每次创建新迁移并执行Update-Database时,密码会被迁移里的值重置。
现有代码
public class ApplicationUserConfiguration : IEntityTypeConfiguration<ApplicationUser>
{
    public void Configure(EntityTypeBuilder<ApplicationUser> builder)
    {
        PasswordHasher<ApplicationUser> passwordHasher = new PasswordHasher<ApplicationUser>();

        // User1
        ApplicationUser user = new ApplicationUser
        {
            Id = "f3a6223f-ac2d-47d9-87d7-e297ea1b78e4",
            ConcurrencyStamp = "f3a6223f-ac2d-47d9-87d7-e297ea1b78e4",
            Email = "abc@gmail.com",
            NormalizedEmail = "ABC@GMAIL.COM",
            EmailConfirmed = true,
            FirstName = "User1",
            LastName = "XYZ",
            UserName = "abc@gmail.com",
            NormalizedUserName = "ABC@GMAIL.COM",
        };
        
        user.PasswordHash = passwordHasher.HashPassword(user, "Apple@123");
        builder.HasData(user);

        // User 2
        user = new ApplicationUser
        {
            Id = "c061e936-bfc7-4b2a-95d1-644f2c19037a",
            ConcurrencyStamp = "c061e936-bfc7-4b2a-95d1-644f2c19037a",
            Email = "def@gmail.com",
            NormalizedEmail = "DEF@GMAIL.COM",
            EmailConfirmed = true,
            FirstName = "User2",
            LastName = "XYZ",
            UserName = "def@gmail.com",
            NormalizedUserName = "DEF@GMAIL.COM",
        };

        user.PasswordHash = passwordHasher.HashPassword(user, "Banana@123");
        builder.HasData(user);          
    }
}

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {

    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.ApplyConfiguration(new ApplicationUserConfiguration());          
    }
}
解决方案

问题根源

builder.HasData配置的种子数据会被EF Core视为模型的一部分,每次生成迁移时都会校验这些数据,且执行迁移时会强制覆盖数据库中对应主键的记录,这就是两个问题的核心原因。

解决步骤

1. 移除实体配置类中的种子数据

修改ApplicationUserConfiguration,删除所有builder.HasData相关代码,这个类仅用于配置实体的属性映射逻辑(如果有需要),不再存放种子数据:

public class ApplicationUserConfiguration : IEntityTypeConfiguration<ApplicationUser>
{
    public void Configure(EntityTypeBuilder<ApplicationUser> builder)
    {
        // 这里只保留实体属性映射配置,比如字段长度、索引等
        // 移除所有builder.HasData代码
    }
}

2. 新增条件式种子初始化逻辑

在ApplicationDbContext中添加种子初始化方法,通过检查数据库中是否存在目标用户,决定是否插入,避免重复操作和覆盖现有数据:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.ApplyConfiguration(new ApplicationUserConfiguration());          
    }

    // 种子用户初始化方法
    public async Task SeedUsersAsync(UserManager<ApplicationUser> userManager)
    {
        var passwordHasher = new PasswordHasher<ApplicationUser>();

        // 检查User1是否存在,不存在则创建
        var user1 = await userManager.FindByIdAsync("f3a6223f-ac2d-47d9-87d7-e297ea1b78e4");
        if (user1 == null)
        {
            user1 = new ApplicationUser
            {
                Id = "f3a6223f-ac2d-47d9-87d7-e297ea1b78e4",
                ConcurrencyStamp = Guid.NewGuid().ToString(),
                Email = "abc@gmail.com",
                NormalizedEmail = "ABC@GMAIL.COM",
                EmailConfirmed = true,
                FirstName = "User1",
                LastName = "XYZ",
                UserName = "abc@gmail.com",
                NormalizedUserName = "ABC@GMAIL.COM",
            };
            user1.PasswordHash = passwordHasher.HashPassword(user1, "Apple@123");
            await userManager.CreateAsync(user1);
        }

        // 检查User2是否存在,不存在则创建
        var user2 = await userManager.FindByIdAsync("c061e936-bfc7-4b2a-95d1-644f2c19037a");
        if (user2 == null)
        {
            user2 = new ApplicationUser
            {
                Id = "c061e936-bfc7-4b2a-95d1-644f2c19037a",
                ConcurrencyStamp = Guid.NewGuid().ToString(),
                Email = "def@gmail.com",
                NormalizedEmail = "DEF@GMAIL.COM",
                EmailConfirmed = true,
                FirstName = "User2",
                LastName = "XYZ",
                UserName = "def@gmail.com",
                NormalizedUserName = "DEF@GMAIL.COM",
            };
            user2.PasswordHash = passwordHasher.HashPassword(user2, "Banana@123");
            await userManager.CreateAsync(user2);
        }
    }
}

3. 在程序启动时执行种子初始化

在Program.cs中,添加启动时的种子数据执行逻辑:

var app = builder.Build();

// 执行种子数据初始化
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
    var dbContext = services.GetRequiredService<ApplicationDbContext>();
    
    // 确保数据库已创建
    await dbContext.Database.EnsureCreatedAsync();
    // 执行用户种子初始化
    await dbContext.SeedUsersAsync(userManager);
}

// 其他中间件配置...
app.Run();

4. 清理现有迁移中的用户数据

手动修改之前生成的迁移文件,删除Up和Down方法中关于用户的插入/删除代码,避免后续执行迁移时再次操作这些用户。

效果说明

  • 不再通过HasData绑定用户数据到模型,生成新迁移时EF Core不会再检测这些数据,解决了迁移文件重复包含用户信息的问题;
  • 种子初始化时先检查用户是否存在,仅在不存在时插入,不会覆盖已存在的用户记录,修改后的密码不会被重置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 15:15:34