EF7实施TPH + M2M

时间:2016-05-03 20:50:04

标签: c# entity-framework entity-framework-core

有没有更好的方法来实现这个最终目标,即在同一个表中存储相关多对多实体的易于查询(和包含能力)的横截面?

我开始时没有在连接表中实现TPH,但是这使得在查询中使用一种或另一种类型更具参与性。

// table Related: [Id]
public class Related
{
    public Guid Id { get; set; }
    public List<RelatedOther> RelatedOthers { get; set; } = new List<RelatedOther>();
    public List<RelatedOtherOne> RelatedOtherOnes { get; set; } = new List<RelatedOtherOne>();
    public List<RelatedOtherTwo> RelatedOtherTwos { get; set; } = new List<RelatedOtherTwo>();
}

// table RelatedOther: [RelatedId, OtherId, Type]
public abstract class RelatedOther
{
    public Guid RelatedId { get; set; }
    public Guid OtherId { get; set; }

    public Related Related { get; set; }
    public Other Other { get; set; }
    public abstract RelatedOtherType Type { get; }
}
public class RelatedOtherOne : RelatedOther
{
    public override RelatedOtherType Type => RelatedOtherType.One;
    // should be unnecessary, 'Other' should be correct type
    public OtherOne OtherOne { get; set; }
}
public class RelatedOtherTwo : RelatedOther
{
    public override RelatedOtherType Type => RelatedOtherType.Two;
    // should be unnecessary, 'Other' should be correct type
    public OtherTwo OtherTwo { get; set; }
}
public enum  RelatedOtherType : int
{
    One = 1,
    Two = 2
}

// table Other: [Id, OneProp, TwoProp]
public abstract class Other
{
    public Guid Id { get; set; }
    public List<RelatedOther> RelatedOthers { get; set; } = new List<RelatedOther>();
}
public class OtherOne : Other
{
    public string OneProp { get; set; }
}
public class OtherTwo : Other
{
    public string TwoProp { get; set; }
}

TPH已映射like this
M2M在HasKey()

中映射like this +鉴别符

当“相关”实体演变为像“其他”这样的TPH策略时,这变得更加复杂(如果不是不可能的话?)。

1 个答案:

答案 0 :(得分:0)

我没有简单的解决方案但是当我偶然发现同样的问题时,我认为我将分享到目前为止我所拥有的一切。

我发现我通常需要将所有或许多类型的关系加载到TPH结构的类中。

所以我使用基多对多类来加载相关对象。因此,这个类不能是抽象的:

public class Event2Location
{
    [Required]
    public Event Event { get; set; }
    public int EventId { get; set; }

    [Required]
    public Location Location { get; set; }
    public int LocationId { get; set; }

    public byte EntityType { get; set; }
}

派生类仅添加一些属性以便于访问:

public class Event2Country : Event2Location
{
    [NotMapped]
    public Country Country
    {
        get { return base.Location as Country; }
        set { base.Location = value; }
    }

    [NotMapped]
    public int CountryId
    {
        get { return base.LocationId; }
        set { base.LocationId = value; }
    }
}

Event课程中我有:

public virtual ICollection<Event2Location> Event2Locations { get; set; }

[NotMapped]
public virtual ICollection<Event2Country> Event2Countries => Event2Locations?.OfType<Event2Country>().ToList();
// I should probably add some caching here if accessed more often

[NotMapped]
public virtual ICollection<Event2City> Event2Cities => Event2Locations?.OfType<Event2City>().ToList();

所以当我加载连接表时,我可以使用

.Include(e => e.Event2Locations).ThenInclude(j => j.Location)

我可以根据需要使用NotMapped Collections访问特定类型的关系。

我仍然使用派生的Event2 ...类添加新关系。

如您所见,我已将EntityType列添加到用作TPH鉴别器的多对多类中。如果我不想加载所有这些,我还可以声明要加载哪些类型的关系/实体。

modelBuilder.Entity<Event2Location>()
    .HasDiscriminator<byte>("EntityType")
    .HasValue<Event2Location>(0)
    .HasValue<Event2Country>(1)

这肯定远非完美,但我终于放弃了优化。首先,EFCore必须变得更加成熟。其次,我想看看我是如何实际使用这些结构的。

PS:实际上我的位置TPH结构中有父子关系。在这里,我没有为关系类创建TPH结构(如你所说 - 不可能或至少不合理)。我添加了ParentType和ChildType。因此,我可以确定我实际想要加载哪些关系。然后,我从结果中获取客户端手动需要的类型的相关位置。