EF6:包含在SQL Server中的嵌套TPH结构

时间:2017-01-05 15:59:33

标签: c# eager-loading

大家好,2017年新人快乐,

我有以下表格/对象结构。

[Table("Table1")]
public class Table1
{
    [Key]
    public long Table1Id { get; set; }

    public virtual ICollection<Table2> ItemsOfTable2 { get; set; }
}

[Table("Table2")]
public class Table2
{
    [Key]
    public long Table2Id { get; set; }

    public long Table1Id { get; set; }

    [ForeignKey("Table1Id")]
    public virtual Table1 Table1Object { get; set; }

    public virtual ICollection<Table3Base> ItemsOfTable3 { get; set; }

    [NotMapped]
    public virtual ICollection<Table3Red> RedItems
    {
        get { return this.ItemsOfTable3.OfType<Table3Red>().ToList(); }
    }

    [NotMapped]
    public virtual ICollection<Table3Blue> BlueItems
    {
        get { return this.ItemsOfTable3.OfType<Table3Blue>().ToList(); }
    }
}

[Table("Table3Base")]
public abstract class Table3Base
{
    [Key]
    public long Table3Id { get; set; }

    public long Table2Id { get; set; }

    [ForeignKey("Table2Id")]
    public virtual Table2 Table2Object { get; set; }
}

public class Table3Red : Table3Base
{
    public string SpecialPropertyForRed { get; set; }
}

public class Table3Blue : Table3Base
{
    public int SpecialPropertyForBlue { get; set; }

    public virtual ICollection<Table4> ItemsOfTable4 { get; set; }
}

[Table("Table4")]
public class Table4
{
    [Key]
    public long Table4Id { get; set; }

    public long Table3Id { get; set; }

    [ForeignKey("Table3Id")]
    public virtual Table3Blue Table3BlueObject { get; set; }
}

public class MyContext : DbContext
{
    public virtual IDbSet<Table1> Table1DbSet { get; set; }
    public virtual IDbSet<Table2> Table2DbSet { get; set; }
    public virtual IDbSet<Table3Red> Table3RedDbSet { get; set; }
    public virtual IDbSet<Table3Blue> Table3BlueDbSet { get; set; }
    public virtual IDbSet<Table4> Table4DbSet { get; set; }
}

因此,在这个“树”的中间,有一个TPH结构(类Table3Base,Table3Red,Table3Blue存储在数据库表“Table3Base”中)。我们只有Table3Red和Table3Blue的IDbSets,而不是Table3Base。每个对象都有下一个表对象的集合导航属性。

Class Table3Blue具有Table4对象项的另一个集合导航属性。

进一步(但希望无关)信息:默认鉴别器映射到内部枚举:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

    // TPH: Map Standard-Discriminator to Enum
    modelBuilder.Entity<Table3Base>()
        .Map<Table3Red>(m => m.Requires("Typ").HasValue((int)Table3TypEnum.Red))
        .Map<Table3Blue>(m => m.Requires("Typ").HasValue((int)Table3TypEnum.Blue));
}

由于性能问题(逐个加载每个记录非常慢;延迟加载处于活动状态),我们希望通过包含这样的结构从表1到表4读取此结构:

var table1Records = this.m_Context.Table1DbSet
    .Include(t => t.ItemsOfTable2)
    .Include(t => t.ItemsOfTable2.Select(t2 => t2.ItemsOfTable3))
    .Include(t => t.ItemsOfTable2.Select(t2 => t2.ItemsOfTable3.OfType<Table3Blue>().Select(t3 => t3.ItemsOfTable4)))
    .ToList();

第一个和第二个包括似乎工作,但第三个包括抛出一个Argument异常“Include路径表达式必须引用在类型上定义的导航属性。使用虚线路径作为参考导航属性,Select运算符用于集合导航属性。 Parametername:path“。

我做错了什么?如何在前往数据库的途中包含Table4对象?

亲切的问候,伴侣

1 个答案:

答案 0 :(得分:0)

这是我们的解决方法。我怀疑这是最好的解决方案,所以更好的方式受到高度赞赏......

Table3Base将集合导航属性作为虚拟属性获取到Table4。

Table3Red(没有Table4对象的对象)使用getter返回此属性来覆盖Table4对象的空列表,而不是setter。

因此,我们可以将Include级联到Table4而不进行任何类型检查。在我们的PTH数据库表中没有不必要的记录&#34; Table3Base&#34;。所以一切都很好,除了具有未使用的导航属性的Table3Red的笨拙定义。 : - (

BTW:包含长路径包括沿此路径的所有对象,因此显式&#34; .Include(A).Include(A.B).Include(A.B.C)&#34;没有必要; &#34; .INCLUDE(A.B.C)&#34;也会这样做。代码示例中的迭代.Include是为了清楚起见。

HTH,Mate