我可以使用 Entity Framework 4 CTP5 访问 TPH 映射中的鉴别器值吗

Posted

技术标签:

【中文标题】我可以使用 Entity Framework 4 CTP5 访问 TPH 映射中的鉴别器值吗【英文标题】:Can I access the discriminator value in TPH mapping with Entity Framework 4 CTP5 【发布时间】:2010-12-24 11:06:18 【问题描述】:

使用 Entity Framework 4 CTP5 Code First 和 this example

是否可以访问判别器值?

我想在像这样的投影中使用它

context.BillingDetails.Select(x => new  Number = x.Number, DiscrimitatorValue = /* how do I get the discriminator value? */ );

来自this post,我了解到鉴别器无法映射到属性,但还有其他访问方式吗?

【问题讨论】:

不——你不能。判别器被排除在模型之外。想要在投影中使用它的原因是什么?你能举一个例子要求/查询你试图实现的目标吗? @RPM1984 我想将我的查询投影到一个包含来自连接表的信息的新类型,但如果我这样做,我将丢失我的原始类型信息以供记录。返回的对象将是我的新类型,我将无法识别它们的原始类型。我希望我可以将鉴别器值投射到新类型上,从而解决问题 同样的问题..想要选择所有记录及其类型。也许有人知道解决方法? EF 对 TPH 的支持很糟糕。任何公共映射 API 都不支持它。您可以通过一些类获得映射,但属性甚至类型通常是内部的,因此无法使用。它位于名为 Configuration 的 EdmType 的 MetadataProperties 集合条目中,然后在内部属性 SubTypeMappingConfigurations 下,您可以找到鉴别器,最终它们的值隐藏在更多声明为“内部”的属性中。完全没用。我为什么还要费心迁移到 EF Core,它对此的支持比零还差。 【参考方案1】:

我可能在这个游戏上迟到了,但我只是在返回当前类型名称的基类中添加了一个 getter 属性:

public string DiscriminatorValue 
    get 
        return this.GetType().Name;
    

由于默认情况下 EF 将在鉴别器字段中使用相同的值,因此它们将匹配。

【讨论】:

除非你覆盖它;-) 我在这里遗漏了什么吗?这在IQueryable.Select 中不起作用,这是 OP 要求的。 NotSupportedException: The specified type member 'DiscriminatorValue' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported..【参考方案2】:

在 EF Core 2.1(我没有检查过以前的版本)中,将 Discriminator 添加到基本抽象类作为私有集属性就足够了。它将被映射为足够的值。

public abstract class Entity

    public int Id  get; set; 
    public string Discriminator  get; private set; 

EF 本身会自动将适当的鉴别器值插入数据库,并在读取时自动将其设置为对象。

【讨论】:

但是你能读回这个基本类型吗?您可以通过这种方式获得鉴别器值,但它有什么用呢?仍然无法从“select * from TPHTable”或 sproc 或任何有用的查询中读取 TPH 类型。只是得到“无法实例化抽象类”错误。这是 EF Core 2.1 能做到的最好的吗?完全缺乏对 TPH 类型的任何支持,就像 EF6 一样,甚至完全放弃了 EF6 对 TPT 和 TPC 的支持?哇。再见。 关于 EF 以前的版本,我可以确认这在 EF6 中不起作用。它只会尝试创建第二个Discriminator1 列。 您可以在 EF Core 中添加一个与您为鉴别器指定的名称相同的属性,例如:...HasDiscriminator("Type")【参考方案3】:

在Morteza Manavi in the comments of his post 提供更多信息后,简单的答案是否定的

您应该知道,鉴别器列由 Code First 内部使用,您无法从继承映射的角度读取/写入其值。

要访问鉴别器,我必须对数据库执行 SqlQuery 或更改我的映射策略。

【讨论】:

是的,执行 SqlQuery 以获取鉴别器,然后用它做什么?您无法获得鉴别器的值,即使您可以,那又如何……手动映射每个子类型?没办法。【参考方案4】:

撇开原因不谈,我最近遇到了同样的问题,但我相信这仍然与 v4 of the EF Framework 有关。

首先,创建一个将鉴别器值选择为两列的视图。

create view dbo.vw_BillingDetail
as
    select BillingDetailId, DiscriminatorValue, DiscriminatorValue as DiscriminatorValue2 from dbo.BillingDetail
go

其次,在上下文创建期间将视图映射到您的实体:

modelBuilder
    .Entity<BillingDetail>()
    .HasKey(n => n.BillingDetailId)
    .Map(map =>
    
        map.ToTable("vw_Person");
    )

第三,使用视图中的列之一为派生类定义鉴别器映射:

.Map<MyDerivedBillingDetail>(map =>

    map.Requires("DiscriminatorValue2").HasValue("YourValue");
)

最后,为视图中的另一个鉴别器列定义一个 getter 和一个私有 setter,并将 DatabaseGenerated 注释设置为 Computed,以防止 EF 更新/插入该字段:

class BillingDetail

    public BillingDetailId  get; set; 

    [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
    public DiscriminatorValue  get; private set; 

您可以将私有设置器更改为受保护,并在派生实体的构造期间显式设置此值,以便鉴别器在被持久化之前具有值:

class MyDerivedBillingDetail : BillingDetail

    public MyDerivedBillingDetail()
    
        this.DiscriminatorValue = "MyValue";
    

【讨论】:

【参考方案5】:

扩展 @Michael Black 对 Entity Framework Core 2.1 的回答(早期?在 2.1.4 中测试)

您可以使用任何您想要的属性名称、数据库字段名称和数据类型。

创建属性:

[Column("foo_type_id")]
class Foo 
    public FooTypesEnum TypeId get; set;

然后在您的上下文类中通过 modelBuilder 使用流利的 API:

modelBuilder.Entity<Foo>(b => 
    b.HasDiscriminator(foo => foo.TypeId)
        .HasValue<SubFooA>(FooTypesEnum.SubFooA)
        .HasValue<SubFooB>(FooTypesEnum.SubFooB);
);

如果您需要构建可组合查询(例如,在鉴别器上分组等),这非常有用。

【讨论】:

【参考方案6】:

您为什么不改用以下查询?

 var q = con.BillingDetails.OfType<BankAccount>().ToList();

【讨论】:

以上只是一个例子。我想在哪里使用它需要一个投影,我想知道原始类型是什么。另外,我只想提高我的知识。 @Devart 我可以使用con.BillingDetails.OfType&lt;baseclass&gt;().ToList() 我只想要“基类”对象的列表。 @yogendarji 如果 BillingDetail 是一个抽象类,则不能有此类的实例,并且 con.BillingDetails.OfType().ToList() 或 con.BillingDetails.ToList() 没有有道理。如果 BillingDetail 不是抽象的,请尝试查询 con.BaseClass.Where(t => !(t is FirstChildClass || t is SecondChildClass || ... || t is LastChildClass)).ToList(),即 con.BillingDetails。其中(t => !(t 是 BankAccount || t 是 CreditCard)).ToList().【参考方案7】:

您可以使用您在 EF Core 中为鉴别器指定的名称添加属性。示例:

在 DBContext 中:

...HasDiscriminator<string>("Type")..

在基类中做:

public string Type  get; private set; 

【讨论】:

以上是关于我可以使用 Entity Framework 4 CTP5 访问 TPH 映射中的鉴别器值吗的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 4.1 Code First - 使用 LinqKit PredicateBuilder 时忽略包含

Entity Framework 4 表值函数

使用 MySQL 时,为啥 Entity Framework 4 试图将 long 转换为小数?

Entity Framework 4 ste删除外键关系

在Entity Framework 4中使用存储库/ uow模式的多个ObjectContexts

在 .Net 3.5 应用程序中使用 Entity Framework 4.0 [重复]