如何在 EF Core 中自动映射 TPH Derived 类?

How to Automatically Map TPH Derived Classes in EF Core?

默认情况下,EF6 会映射一个基础抽象 class,它是为 Table 每个层次结构 (TPH) 派生的 classes。

EF Core 不再遵循此逻辑并要求派生 classes 被选择加入。文档指出:

By convention, types that are exposed in DbSet properties on your context are included in the model as entities. Entity types that are specified in the OnModelCreating method are also included, as are any types that are found by recursively exploring the navigation properties of other discovered entity types.

如果您有几个子类型,使用这种方法并不难遵循,因为您可以将它们添加为 DbSet 或为每个子类型添加一个 HasDiscriminator().HasValue(),映射如下:

builder.HasDiscriminator()
    .HasValue<CommaSymbolRule>("CommaSymbolRule")
    .HasValue<DashSymbolRule>("DashSymbolRule")
    .HasValue<IsNumericSymbolRule>("IsNumericSymbolRule")
    .HasValue<IsPunctuationSymbolRule>("IsPunctuationSymbolRule")
    .HasValue<PeriodSymbolRule>("PeriodSymbolRule")

在某些情况下,这是次优的,因为您可能有许多派生的 classes。就我而言,我有一个规则引擎,不想单独映射每个规则。

有没有办法在 EF Core Table Per Hierarchy 场景中自动映射基础 class 的子类型,而无需手动添加它们?

我以为在 EF Core 中可能有一种方法可以做到这一点,但发现没有。

我与 EF 团队讨论了为什么自动选择加入不再是默认设置,他们对可能非常有效的“程序集扫描解决方案”的稳定性表示担忧。目前,他们似乎对按照这些思路添加新功能不太感兴趣。

这是我想到的。它将程序集扫描放在映射代码中,但似乎有效。

首先,我创建了一个扩展方法来获取派生的 classes(它可以根据名称选择性地忽略特定类型):

public static Type[] GetDerivedClasses(this Type type, string[] ignoreTypeNames = null) 
{
    ignoreTypeNames = ignoreTypeNames ?? new string[0];

    return Assembly.GetAssembly(type)
                    .GetTypes()
                    .Where
                    (
                        t => t.IsSubclassOf(type) &&
                        (!ignoreTypeNames?.Any(t.Name.Contains) ?? false)
                    )
                    .OrderBy(o => o.Name)
                    .ToArray();
}

然后对 EF Core 映射中的基础 class 使用类似于此的代码,切换您的类型(即:此代码中的“SymbolRule”):

public void Configure(EntityTypeBuilder<SymbolRule> builder)
{
    builder.ToTable("SymbolRule");       // my example table
    builder.HasKey(t => t.SymbolRuleId); // my example key


    foreach (var type in typeof(SymbolRule).GetDerivedClasses())
    {
        builder.HasDiscriminator()
               .HasValue(type, type.Name);
    }
}

foreach 从基础 class 获取派生的 classes 并循环遍历它们并为每个添加一个鉴别器类型。