#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 aParent
, разница только в том, когда вы хотите попросить EF получить данные из базы данных. Независимо от использования запроса LINQ, EF всегда использует SQL для получения данных