2016-05-03 2 views
1

Есть ли лучший способ достичь этой конечной цели - иметь легко запрашиваемые (и Include-able) сечения связанного объекта «многие ко многим», хранящиеся в одной и той же таблице?EF7 Внедрение TPH + M2M

Я начал без внедрения TPH в таблице соединений, но это делает потребление одного типа или другого в более привлекательных запросах, afaict.

// 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 отображается like this + дискриминатор в HasKey()

Это становится еще более сложным (если не невозможно?), Когда объект в 'связанных с' перерастает в стратегию TPH как другой'.

ответ

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. Таким образом, я могу определить, какие отношения я действительно хочу загрузить. Затем я получаю связанные местоположения типов, которые мне нужны вручную на стороне клиента из результата.

+0

Я решил, что если концы связи m2m являются TPH, тогда сама таблица соединений также должна отражать эту TPH, независимо от того, открываю ли я свойства навигации на обоих концах для всех этих типов TPH. Это просто более чистый дизайн. Затем это позволит платформе обрабатывать перевод свойства навигации, который у вас есть как «[NotMapped]» с использованием трансляции. – JoeBrockhaus

+0

И когда я использую 'abstract' в моем базовом классе, это не мешает мне запрашивать только этот базовый тип - если запрос приводит к более чем одному производному типу в качестве результирующего набора, EF будет создавать экземпляр правильного производного типа и экземпляр в соответствующем производном типе будет автоматически заполнен этим экземпляром. Если ваша модель имеет 2 (и только 2) производных типа, то базовый класс должен быть абстрактным. Экземпляр Event2Location без значения дискриминатора EventType на самом деле не имеет смысла, правильно? Поэтому вы никогда не сможете создать ванильный экземпляр Event2Location. – JoeBrockhaus