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
相关产品推荐
相关产品推荐

