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

EF Core无法将鉴别器列映射到现有枚举列的问题解决

EF Core 8 使用枚举作为TPH鉴别器时避免生成影子Discriminator属性

我在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属性。

修复步骤:

  1. 移除BaseItemTypeConfiguration中多余的Configure(builder);调用
  2. 确保鉴别器配置正确绑定到实体的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 01:20:54