EF Core无法将鉴别器列映射到现有枚举列的问题解决
我在EF Core 8.0.2中配置Type-per-Hierarchy(每个层次结构一个表)时,希望使用枚举作为鉴别器。代码运行输出符合预期,但数据库架构不符合预期:EF Core未使用已有的Type列,反而新增了一个名为Discriminator的文本类型影子属性,同时填充Type列的枚举整数值和Discriminator列的枚举字符串值。
日志中出现提示:
The property 'BaseItem.Discriminator' was created in shadow state because there are no eligible CLR members with a matching name.
我的C#代码
using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using var context = new TestDbContext(); await context.Database.EnsureDeletedAsync(); await context.Database.EnsureCreatedAsync(); await context.SaveChangesAsync(); var items = await context.Items.ToListAsync(); foreach (var item in items) { Console.WriteLine(item.ToString()); } public enum BaseItemType { ConcreteA = 0, ConcreteB = 1 } public abstract class BaseItem { public int Id { get; set; } public string Name { get; set; } public BaseItemType Type { get; set; } } public class ConcreteItemA : BaseItem { public ConcreteItemA() { Type = BaseItemType.ConcreteA; } public int Number { get; set; } public override string ToString() => $"{Id,3}|{Name,10}|{Number,10:G2}"; } public class ConcreteItemB : BaseItem { public ConcreteItemB() { Type = BaseItemType.ConcreteB; } public decimal Amount { get; set; } public override string ToString() => $"{Id,3}|{Name,10}|{Amount,10:C2}"; } public class TestDbContext : DbContext { public DbSet<BaseItem> Items => Set<BaseItem>(); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseSqlite("Data Source=items.db;Cache=Shared") .LogTo(Console.WriteLine, LogLevel.Debug) .EnableDetailedErrors(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(GetType().Assembly); } } public abstract class BaseItemTypeConfiguration : IEntityTypeConfiguration<BaseItem> { public void Configure(EntityTypeBuilder<BaseItem> builder) { builder.ToTable("Items") .HasDiscriminator<BaseItemType>("Type") .HasValue<ConcreteItemB>(BaseItemType.ConcreteB) .HasValue<ConcreteItemA>(BaseItemType.ConcreteA) .IsComplete(); builder.HasKey(b => b.Id); builder.Property(b => b.Id).IsRequired().ValueGeneratedOnAdd(); Configure(builder); } } public class ConcreteItemATypeConfiguration : IEntityTypeConfiguration<ConcreteItemA> { public void Configure(EntityTypeBuilder<ConcreteItemA> builder) { builder.Property(b => b.Number).HasDefaultValue(42); builder.HasData(new ConcreteItemA { Id = 1, Name = "A", Number = 41 }); } } public class ConcreteItemBTypeConfiguration : IEntityTypeConfiguration<ConcreteItemB> { public void Configure(EntityTypeBuilder<ConcreteItemB> builder) { builder.Property(b => b.Amount).HasDefaultValue(42.0M); builder.HasData(new ConcreteItemB { Id = 2, Name = "B", Amount = 41.0M }); } }
EF Core生成的SQL(创建表)
CREATE TABLE "Items" ( "Id" INTEGER NOT NULL CONSTRAINT "PK_Items" PRIMARY KEY AUTOINCREMENT, "Name" TEXT NOT NULL, "Type" INTEGER NOT NULL, "Discriminator" TEXT NOT NULL, "Number" INTEGER NULL DEFAULT 42, "Amount" TEXT NULL DEFAULT '42.0' ); INSERT INTO "Items" ("Id", "Discriminator", "Name", "Number", "Type") VALUES (1, 'ConcreteItemA', 'A', 41, 0); SELECT changes(); INSERT INTO "Items" ("Id", "Amount", "Discriminator", "Name", "Type") VALUES (2, '41.0', 'ConcreteItemB', 'B', 1); SELECT changes();
我尝试过修改BaseItemTypeConfiguration中的配置,但未成功:
builder.ToTable("Items") .HasDiscriminator(b => b.Type) .HasValue<ConcreteItemB>(BaseItemType.ConcreteB) .HasValue<ConcreteItemA>(BaseItemType.ConcreteA) .IsComplete();
请问如何阻止EF Core添加这个影子Discriminator属性?
解决方案
问题根源在于BaseItemTypeConfiguration的Configure方法中存在递归调用:你在方法末尾又调用了Configure(builder);,这会导致EF Core重复执行配置逻辑,覆盖了之前设置的鉴别器关联,最终触发默认行为生成影子Discriminator属性。
修复步骤:
- 移除
BaseItemTypeConfiguration中多余的Configure(builder);调用 - 确保鉴别器配置正确绑定到实体的
Type属性
修改后的BaseItemTypeConfiguration代码:
public abstract class BaseItemTypeConfiguration : IEntityTypeConfiguration<BaseItem> { public void Configure(EntityTypeBuilder<BaseItem> builder) { builder.ToTable("Items") .HasDiscriminator<BaseItemType>(b => b.Type) .HasValue<ConcreteItemB>(BaseItemType.ConcreteB) .HasValue<ConcreteItemA>(BaseItemType.ConcreteA) .IsComplete(); builder.HasKey(b => b.Id); builder.Property(b => b.Id).IsRequired().ValueGeneratedOnAdd(); } }
验证效果:
重新生成数据库后,表结构中只会保留Type列作为鉴别器,不会出现额外的Discriminator列;EF Core生成的SQL查询也会使用Type列来区分不同的实体类型,完全符合预期。
内容的提问来源于stack exchange,提问作者David Keaveny

