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

ASP.NET Core中EF Core 7迁移失败:派生键配置问题

EF Core 7迁移创建失败:派生类型键配置错误

报错信息

无法在'FoosballUser'上配置键,因为它是派生类型。键必须在根类型'AppUser'上配置。若无意将'AppUser'纳入模型,请确保它未被上下文的DbSet属性引用、未在ModelBuilder配置调用中引用,也未被模型中类型的导航属性引用。

场景说明

有Foodplan和Foosball两个业务区域,各自使用独立DbContext,用户类均继承自基类AppUser(继承IdentityUser)。Foodplan区域可正常创建迁移并更新数据库,但Foosball区域始终出现上述错误,与服务注册顺序、迁移创建顺序无关。目标是让两个区域使用相同登录架构但独立数据库。

现有代码结构

基类AppUser

public class AppUser : IdentityUser
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
    public DateTime DeletedAt { get; set; }
}

FoodUser(继承AppUser)

public class FoodUser : AppUser
{
    // Empty, in case I want to add attributes in the future
}

FoosballUser(继承AppUser)

public class FoosballUser : AppUser
{
    public string Initials { get; set; }
    public string Name { get; set; }
    public string HexCode { get; set; }
    public List<MatchResult>? GameResults { get; set; }
    public int WinStreak { get; set; }
    public int LossStreak { get; set; }
    public List<Team> Teams { get; set; }
}

FoodContext

public class FoodContext : IdentityDbContext<FoodUser>
{
    // Dbsets omitted, non related to the user class.

    public FoodContext(DbContextOptions<FoodContext> options) : base(options)
    {
    }

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

FoosballContext

public class FoosballContext : IdentityDbContext<FoosballUser>
{
    // Dbsets omitted, non related to the user class.

    public FoosballContext(DbContextOptions<FoosballContext> options) : base(options)
    {
    }

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

服务注册代码

// Add the database context
builder.Services.AddDbContext<FoodContext>(dbContextOptions => dbContextOptions.UseMySql(connectionString, serverVersion));

builder.Services.AddDbContext<FoosballContext>(dbContextOptions => dbContextOptions.UseMySql(foosballConnectionString, serverVersion));

// Add identity
builder.Services.AddIdentity<AppUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = false)
                .AddEntityFrameworkStores<FoosballContext>()
                .AddDefaultTokenProviders()
                .AddRoles<IdentityRole>()
                .AddDefaultUI();

builder.Services.AddIdentity<AppUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = false)
                .AddEntityFrameworkStores<FoodContext>()
                .AddDefaultTokenProviders()
                .AddRoles<IdentityRole>()
                .AddDefaultUI();

问题核心

  • Identity服务注册冲突:连续调用两次AddIdentity<AppUser, IdentityRole>,后一次注册会覆盖前一次,导致FoosballContext的Identity配置被FoodContext覆盖,EF Core无法正确识别FoosballUser的继承关系。
  • 派生类型的模型识别:当Identity绑定根类型AppUser,但DbContext关联的是派生类FoosballUser时,EF Core会将AppUser和FoosballUser视为继承层次结构,而非独立实体,进而触发键配置错误。

解决方案

方案1:为每个DbContext绑定对应派生用户类型注册Identity

每个业务区域的Identity应绑定到各自的用户类和DbContext,而非根类AppUser,这更符合独立业务区域的设计目标:

修改服务注册代码:

// Add the database context
builder.Services.AddDbContext<FoodContext>(dbContextOptions => dbContextOptions.UseMySql(connectionString, serverVersion));

builder.Services.AddDbContext<FoosballContext>(dbContextOptions => dbContextOptions.UseMySql(foosballConnectionString, serverVersion));

// 注册Foodplan区域的Identity,绑定FoodUser和FoodContext
builder.Services.AddIdentity<FoodUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = false)
                .AddEntityFrameworkStores<FoodContext>()
                .AddDefaultTokenProviders()
                .AddRoles<IdentityRole>()
                .AddDefaultUI();

// 注册Foosball区域的Identity,绑定FoosballUser和FoosballContext
builder.Services.AddIdentity<FoosballUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = false)
                .AddEntityFrameworkStores<FoosballContext>()
                .AddDefaultTokenProviders()
                .AddRoles<IdentityRole>()
                .AddDefaultUI();

方案2:显式配置FoosballContext忽略根类AppUser

若坚持使用AppUser作为Identity泛型参数,需在FoosballContext的OnModelCreating中显式忽略AppUser,让EF Core仅识别FoosballUser:

修改FoosballContext:

public class FoosballContext : IdentityDbContext<FoosballUser>
{
    // Dbsets omitted, non related to the user class.

    public FoosballContext(DbContextOptions<FoosballContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // 显式忽略根类AppUser,避免EF Core将其纳入继承层次
        builder.Ignore<AppUser>();
    }
}

额外注意事项

  • 创建迁移时需指定对应DbContext:
    # 创建Foodplan迁移
    Add-Migration InitialCreate -Context FoodContext
    # 创建Foosball迁移
    Add-Migration InitialCreate -Context FoosballContext
    
  • 确保每个DbContext的连接字符串指向独立数据库,避免数据混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 03:31:37