【问题标题】:How can I mix TPH and TPT in Entity Framework 6?如何在 Entity Framework 6 中混合 TPH 和 TPT?
【发布时间】:2014-11-26 10:16:44
【问题描述】:

初步情况

我有一个使用现有数据库的应用程序,目前使用 NHibernate 作为 O/R-Mapper。
现在我需要使用 Code FirstFluent API Configuration 迁移到 Entity Framework 6.1.1

但是现在我对数据模型的一部分有问题,因为它使用了不同类型的继承策略(TPT 和 TPH)

结构

注意:在这里发布完整的数据模型对我来说似乎有点太大了,所以我在一个小型 POC 程序中重现了我面临的问题。

CLASS                  | TABLE              | TYPE
-----------------------+--------------------+------
BaseEntity (abstract)  | BaseTable          |
Inherited_TPH          | BaseTable          |  1
Inherited_TPT          | Inherited_TPT      |  2

表中用作鉴别器的列称为Type

基于this answer我添加了一个抽象类Intermediate_TPH作为中间层:

一些示例数据:带有ID=3 的条目属于Inherited_TPT 类型

代码

这些是我的实体类和我的上下文类:

class MyContext : DbContext
{
    public MyContext ( string connectionString )
        : base ( connectionString )
    {
    }

    public DbSet<Inherited_TPH> TPH_Set { get; set; }
    public DbSet<Inherited_TPT> TPT_Set { get; set; }
    public DbSet<SomethingElse> Another_Set { get; set; }

    protected override void OnModelCreating ( DbModelBuilder modelBuilder )
    {
        modelBuilder
        .Entity<BaseEntity> ()
        .ToTable ( "BaseTable" );

        modelBuilder
        .Entity<Inherited_TPH> ()
        .Map ( t => t.Requires ( "Type" ).HasValue ( 1 ) );

        modelBuilder
        .Entity<Intermediate_TPT> ()
        .Map ( t => t.Requires ( "Type" ).HasValue ( 2 ) );

        modelBuilder
        .Entity<Intermediate_TPT> ()
        .Map<Inherited_TPT> ( t => t.ToTable ( "Inherited_TPT" ) ); 

        modelBuilder
        .Entity<SomethingElse> ()
        .ToTable ( "SomethingElse" )
        .HasKey ( t => t.Id );
    }
}

public abstract class BaseEntity
{
    public virtual int Id { get; set; }
    public virtual string Title { get; set; }
}
public class Inherited_TPH : BaseEntity
{
}
public abstract class Intermediate_TPT : BaseEntity
{
}
public class Inherited_TPT : Intermediate_TPT
{
    public virtual string Comment { get; set; }
}
public class SomethingElse
{
    public virtual string Description { get; set; }
    public virtual int Id { get; set; }
}

运行以下代码会报错。

    static void Main ( string[] args )
    {
        Database.SetInitializer<MyContext> ( null );
        var ctx = new MyContext ( @"Data Source=(local);Initial Catalog=nh_ef;Integrated Security=true" );
        try
        {
            // Accessing Inherited_TPH works just fine
            foreach ( var item in ctx.TPH_Set ) Console.WriteLine ( "{0}: {1}", item.Id, item.Title );
            // Accessing Inherited_TPT works just fine
            foreach ( var item in ctx.TPT_Set ) Console.WriteLine ( "{0}: {1} ({2})", item.Id, item.Title, item.Comment );
            // The rror occurs when accessing ANOTHER entity:
            foreach ( var item in ctx.Another_Set ) Console.WriteLine ( "{0}: {1}", item.Id, item.Description );
        }
        catch ( Exception ex )
        {
            Console.WriteLine ( ex.Message );
            if( ex.InnerException != null ) { Console.WriteLine ( ex.InnerException.Message ); }
        }
    }

输出

程序产生以下输出:

1:辛普森
2:约翰逊
3:史密斯(更多关于史密斯的细节)
4:米勒(关于米勒的更多细节)
准备命令定义时出错。有关详细信息,请参阅内部异常。

(26,10):错误 3032:从第 14、26 行开始映射片段时出现问题:EntityTypes PoC.Inherited_TPH、PoC.Inherited_TPT 被映射到表 BaseEntity 中的相同行。映射条件可用于区分这些类型映射到的行。

问题

如您所见,映射似乎工作,因为我可以从Inherited_TPTInherited_TPH 加载所有数据。但是当访问另一个实体时,我得到一个异常

我需要如何配置映射以消除此错误并能够访问现有的数据库结构?

【问题讨论】:

    标签: c# entity-framework inheritance ef-code-first entity-framework-6


    【解决方案1】:

    我终于自己找到了解决方案。我会把它贴在这里以防其他人需要同样的行为......

    • 这种情况下不需要中间层
    • 只有 TPH 需要鉴别器

    所以我们需要做的就是改变映射的定义:

    protected override void OnModelCreating ( DbModelBuilder modelBuilder )
    {
        // Not changed
        modelBuilder
            .Entity<BaseEntity> ()
            .ToTable ( "BaseTable" );
    
    
        // --- CHANGED ---
        modelBuilder.Entity<BaseEntity> ()
            // TPH => Discriminator
            .Map<Inherited_TPH> ( m => m.Requires ( "Type" ).HasValue ( 1 ).IsOptional () ) 
            // TPT => Mapping to table
            .Map<Inherited_TPT> ( m => m.ToTable ( "Inherited_TPT" ) ); 
    
        // Not changed
        modelBuilder
            .Entity<SomethingElse> ()
            .ToTable ( "SomethingElse" )
            .HasKey ( t => t.Id );
    }
    

    【讨论】:

    • “Inherited_TPT”如何知道它必须在类型列中插入2?如果我按照上述方式实施解决方案,我会得到类型是必需列的异常,并且我正在尝试插入一个空值。提前感谢您可以给我的任何反馈:)
    • 自从我不得不处理这个问题已经过去了一段时间,所以我可能记错了 - 但我认为 Type 列应该可以为空,因为 .IsOptional()Inherited_TPT 不需要在Type 中插入一个值,因为它映射到另一个表(Join on Id)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-13
    • 1970-01-01
    • 2016-05-19
    • 2014-08-18
    • 2012-03-18
    相关资源
    最近更新 更多