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

ABP框架7.1修改IdentityUser列后数据种子器报错求助

问题描述

我使用ABP Framework 7.1搭配Angular UI,在DbContext中对IdentityUser的两个列重命名并设为必填:

builder.Entity<IdentityUser>(b =>
{
    b.Property(x => x.Name).IsRequired().HasColumnName("FirstName");
    b.Property(x => x.Surname).IsRequired().HasColumnName("LastName");
});

创建迁移并更新数据库后,列名已成功修改,但运行数据种子器时报错:

Cannot insert null into column name LastName, table AbpUsers, the column does not allow nulls.

我尝试自定义Identity种子器:

[Dependency(ReplaceServices = true)]
public class CustomIdentityDataSeederContributor : IdentityDataSeeder
{
    public CustomIdentityDataSeederContributor(
        IUnitOfWorkManager unitOfWorkManager,
        IGuidGenerator guidGenerator,
        IIdentityRoleRepository roleRepository,
        IIdentityUserRepository userRepository,
        ILookupNormalizer lookupNormalizer,
        IdentityUserManager userManager,
        IdentityRoleManager roleManager,
        ICurrentTenant currentTenant,
        IOptions<IdentityOptions> identityOptions,
        IdentityUserManager identityUserManager)
        : base(guidGenerator, roleRepository, userRepository, lookupNormalizer, userManager, roleManager, currentTenant, identityOptions)
    {
    }

    public override async Task<IdentityDataSeedResult> SeedAsync(string adminEmail, string adminPassword, Guid? tenantId = null)
    {
        var result = new IdentityDataSeedResult();
        Check.NotNullOrWhiteSpace(adminEmail, nameof(adminEmail));
        Check.NotNullOrWhiteSpace(adminPassword, nameof(adminPassword));
        string adminUserName = "custom";
        if (adminEmail == "admin@abp.io")
        {
            return result;
        }
        if (tenantId != Guid.Empty && tenantId != null)
        {
            adminUserName = "custom2";
            if (adminEmail == "custom@Custom.com")
            {
                adminEmail = "custom2@Custom.com";
            }
        }

        using (CurrentTenant.Change(tenantId))
        {
            //"admin" user
            var adminUser = await UserRepository.FindByNormalizedUserNameAsync(
                LookupNormalizer.NormalizeName(adminUserName)
            );

            if (adminUser != null)
            {
                return result;
            }

            adminUser = new IdentityUser(
                GuidGenerator.Create(),
                adminUserName,
                adminEmail,
                tenantId
            )
            {
                //This should be first name and lastname
                Name = adminUserName,
                Surname = "surname"
            };

            (await UserManager.CreateAsync(adminUser, adminPassword)).CheckErrors();
            result.CreatedAdminUser = true;

            //"admin" role
            const string adminRoleName = "admin";
            var adminRole = await RoleRepository.FindByNormalizedNameAsync(LookupNormalizer.NormalizeName(adminRoleName));
            if (adminRole == null)
            {
                adminRole = new IdentityRole(
                    GuidGenerator.Create(),
                    adminRoleName,
                    tenantId
                )
                {
                    IsStatic = true,
                    IsPublic = true
                };

                (await RoleManager.CreateAsync(adminRole)).CheckErrors();
                result.CreatedAdminRole = true;
            }

            (await UserManager.AddToRoleAsync(adminUser, adminRoleName)).CheckErrors();

            return result;
        }
    }
}

注意IdentityUser的属性还是Name和Surname而非新列名,ABP内部类未修改。我还在MigrationService的SeedDataAsync方法里添加了LastName属性:

private async Task SeedDataAsync(Tenant tenant = null)
{
    Logger.LogInformation($"Executing {(tenant == null ? "host" : tenant.Name + " tenant")} database seed...");
   
    await _dataSeeder.SeedAsync(new DataSeedContext(tenant?.Id)
         .WithProperty(IdentityDataSeedContributor.AdminEmailPropertyName, IdentityDataSeedContributor.AdminEmailDefaultValue)
         .WithProperty(IdentityDataSeedContributor.AdminPasswordPropertyName, IdentityDataSeedContributor.AdminPasswordDefaultValue)
         .WithProperty("LastName", "Test")
    );
}

但仍报相同错误,该如何解决?

解决方案

1. 修复自定义种子器的构造函数依赖

你的自定义种子器构造函数多传入了一个IdentityUserManager identityUserManager参数,与基类构造函数的参数列表不匹配,导致ABP无法正确替换默认的IdentityDataSeeder服务,默认种子器仍在执行(默认逻辑不会给Surname赋值,导致数据库LastName列空值)。

修改构造函数,移除多余的参数:

public CustomIdentityDataSeederContributor(
    IGuidGenerator guidGenerator,
    IIdentityRoleRepository roleRepository,
    IIdentityUserRepository userRepository,
    ILookupNormalizer lookupNormalizer,
    IdentityUserManager userManager,
    IdentityRoleManager roleManager,
    ICurrentTenant currentTenant,
    IOptions<IdentityOptions> identityOptions)
    : base(guidGenerator, roleRepository, userRepository, lookupNormalizer, userManager, roleManager, currentTenant, identityOptions)
{
}

2. 调整种子器的邮箱判断逻辑

代码中if (adminEmail == "admin@abp.io") return result;的判断会跳过默认admin用户的创建,如果数据库中不存在该用户,后续其他逻辑可能尝试创建它并触发空值错误。要么移除该判断,要么确保该邮箱对应的用户已提前存在,或者在判断分支内补充赋值逻辑。

3. 验证数据库约束与旧数据

确认AbpUsers表的FirstName和LastName列确实已设置非空约束,并且表中旧数据(如果存在)已填充这两个列的值,避免遗留空值记录触发约束错误。

4. 显式提交UnitOfWork(可选)

在自定义种子器的SeedAsync方法末尾,显式调用UnitOfWork的保存方法,确保数据被正确提交:

await UnitOfWorkManager.Current.SaveChangesAsync();

(需要在构造函数中注入IUnitOfWorkManager并保存为类字段)

5. 重置迁移与数据库(可选)

若问题仍存在,可删除现有迁移记录,重新生成迁移并更新数据库,同时清空AbpUsers表数据,让种子器从头执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 22:05:14