Сопоставление двух свойств одного типа с одной таблицей, в которой есть столбец для различения целевого свойства

#entity-framework #ef-code-first #entity-framework-6

#entity-framework #ef-code-first #entity-framework-6

Вопрос:

У меня есть две таблицы:

 parents
  id

childs
  parent_id
  child_number
  

и два соответствующих типа объектов, где я хочу, чтобы родительский тип имел два свойства навигации, ссылающиеся на первый и второй дочерний элемент:

 class Parent
  property Child1 : Child
  property Child2 : Child
  

Я ищу что-то вроде

 modelBuilder.Entity<Parent>()
  .Map(f => f.Child1, m => m.Requires("child_number").HasValue(0))
modelBuilder.Entity<Parent>()
  .Map(f => f.Child2, m => m.Requires("child_number").HasValue(1))
  

Возможно ли это без наличия двух destinct, производных типов Child1 : Child и Child2 : Child ?

Ответ №1:

Хотя я уверен, что возможно иметь динамический ChildN тип / свойство, я бы выбрал другой подход. Итак, это то, что я собираюсь предложить.

Я думаю, что в вашей ситуации было бы разумнее иметь Parent ссылку на коллекцию дочерних элементов, а не явно иметь свойства для Child1 , Child2 ,…, ChildN .

Я бы сказал, что это не только решает вашу проблему (или, по крайней мере, удовлетворяет вашим потребностям), но и позволит расширить ее в будущем. Если бы вы это сделали, вы бы в конечном итоге обработали выбор 1-го, 2-го, …, n-го дочернего элемента с помощью отдельной части логики.

Вместо этого вы получаете два POCO для родительского и дочернего

 public class Parent()
{
    public Parent() { Children = new HashSet<Child>(); }
    [Required]
    public int Id {get;set}|

    //Collection of Child objects as navigation property
    public virtual ICollection<Child> Children{get;set;}
}


public class Child()
{
    [Required]
    public int ParentId {get;set}
    [Required]
    public int ChildNumber{get;set}

    //Collection of Child objects as navigation property
    public virtual Parent Parent {get; set;}
}
  

Свободное сопоставление API. Этот код Fluent API мог бы также включать [требуемую] информацию как Property s with IsRequired() , но мне было проще вставить ее в определение модели, и это привело бы к переполнению более важной информации о сопоставлении Fluent API.

 mBuilder.Entity<Parent>().HasKey(p=>p.Id);

mBuilder.Entity<Parent>().HasMany(p=>p.Children)
    .WithRequired(c=>c.Parent)
    .HasForeignKey(c=>c.ParentId);

mBuilder.Entity<Child>
    .HasKey(c=> new {c.ParentId, c.ChildNumber});
  

Один из возможных подходов для извлечения N-го дочернего элемента

 GetNthChild(parent, N)
{
    return parent.Children.Where(c => c.ChildNumber ==N);
}
  

Или вы могли бы даже определить свой собственный дочерний класс, который реализует индексатор для получения N-го дочернего элемента; но как вы решите получить N-й дочерний элемент, зависит от вас.

Комментарии:

1. Большое вам спасибо за ваше предложение. Это очень похоже на мою текущую реализацию. Тем не менее, я хотел избежать каких-либо .Where(c => c.ChildNumber == n) запросов и заполнить мои свойства SQL-запросом. Расширяемость не вызывает беспокойства (возможно, мой пример с использованием родительского и дочернего элементов был плохой идеей — класс Bike с двумя свойствами FrontWeel и BackWeel был бы более подходящим)

2. Понятно, я согласен, что было бы неплохо избежать написания LINQ подобным образом. Тем не менее, даже со свойствами навигации (или чем-то более динамичным, как вы хотели), EF по-прежнему выполняет вызов SQL для get Parent.Child , что является частью отложенной загрузки, используемой в EF. Если вы хотите «избежать» вызовов SQL для получения связанного объекта, вы можете отключить отложенную загрузку для Nav Prop, но в этом случае EF загружается Child с помощью SQL-вызова всякий раз, когда вы получаете load a Parent , разница только в том, когда вы хотите попросить EF получить данные из базы данных. Независимо от использования запроса LINQ, EF всегда использует SQL для получения данных