2016-07-15 4 views
3

Я предрек сущность, как это (фактический класс, очевидно, также имеет свойство ID, отображение делается и т.д., но это не проблема, так что я пропустил его здесь):Не поддерживает ли инфраструктура Entity Framework защищенные свойства навигации?

public class Parent 
{ 
    public virtual ICollection<Child> Children {get; set;} 
} 

это работает совершенное:

public class Consumer 
{ 
    void DoBusiness() 
    { 
     using (var ctx = new MyDbContext()) 
     { 
      var entity = ctx.Parents.Find(keyOfParent); 
      // This is as expected: entity.Children refers to a collection which 
      // Entity Framework has assigned, a collection which supports lazy loading. 
     } 
    } 
} 

Теперь я изменить видимость коллекции Дети должны быть защищены:

public class Parent 
{ 
    protected virtual ICollection<Child> Children {get; set;} 
} 

Это приносит неожиданный результат :

public class Consumer 
{ 
    void DoBusiness() 
    { 
     using (var ctx = new MyDbContext()) 
     { 
      var entity = ctx.Parents.Find(keyOfParent); 
      // This is NOT as expected: entity.Children is null. I would expect, that it 
      // had been referring to a collection which Entity Framework would have been 
      // assigning, a collection which should support lazy loading. 
     } 
    } 
} 

Кроме того, если я в ситуации, когда дети защищены попробовать явно загружающие ребенок через:

ctc.Entry(entity).Collection(x => x.Children) 

Тогда я получаю это исключение:

Свойства " Дети "по типу« Родитель »не являются навигационным свойством. Методы Reference и Collection могут использоваться только с навигационными свойствами. Используйте метод Property или ComplexProperty.

Поэтому: Что я должен делать, чтобы иметь защищенное свойство навигации с использованием Entity Framework?

+3

Почему на Земле вы хотели бы это сделать? «Объекты» в EF - это не более чем объектное представление таблиц, записей и отношений базы данных. Они не должны содержать бизнес-логику.Поэтому они и их члены должны быть видимыми для использования в запросах. Образец кода 'ctc.Entry (entity) .Collection (x => x.Children)' может запускаться только из класса 'Parent', что противоречит этим принципам. –

+0

Поскольку конкретные свойства, соответствующие Детям в примере, используются только для размышлений, и поэтому основные соображения, которые вы даете в отношении запросов, не применяются. Что касается вашего комментария к кодировке: Да, очевидно, но это подчеркивает, что Entity Framework не рассматривает свойство как свойство навигации после того, как оно было защищено. У вас также есть какая-то информация, пожалуйста? –

+0

Если свойство помечено 'protected', как будет реализована структура, использующая это свойство и загружающая его? Он не может «видеть» его; только дочерние классы могут. –

ответ

1

Вот как я заработал.

public class Parent 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    internal ICollection<Child> children; 
    protected virtual ICollection<Child> Children { get { return children; } set { children = value; } } 
    internal ICollection<Child> GetChildren() => Children; 
    internal static Expression<Func<Parent, ICollection<Child>>> ChildrenSelector => p => p.Children; 
} 

public class Child 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 

    internal Parent parent; 
    protected virtual Parent Parent { get { return parent; } set { parent = value; } } 
    internal Parent GetParent() => Parent; 
    internal static Expression<Func<Child, Parent>> ParentSelector => c => c.Parent; 
} 

public class MyDbContext : DbContext 
{ 
    public DbSet<Parent> Parents { get; set; } 
    public DbSet<Child> Children { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Entity<Parent>() 
      .HasMany(Parent.ChildrenSelector) 
      .WithRequired(Child.ParentSelector) 
      .Map(a => a.MapKey("ParentId")); 

     base.OnModelCreating(modelBuilder); 
    } 
} 

Явные полей, используемых не являются существенными - Я положил их для того, чтобы иметь возможность наблюдать, что там, вы можете продолжать использовать авто свойства.

Важной частью является предоставление выражений доступа к аксессуарам и их использование с использованием Fluent API. Без явной конфигурации вы получаете поведение, которое вы описываете. С этим все работает.

Например, отложенная загрузка:

var parent = ctx.Parents.Find(keyOfParent); 
var children = parent.GetChildren(); 

или явная загрузка:

var parent = ctx.Parents.Find(keyOfParent); 
db.Entry(parent).Collection("Children").Load(); 
var children = parent.children; 

UPDATE: Довольно странно, если заменить код конфигурации

modelBuilder.Entity<Parent>() 
    .HasMany(Parent.ChildrenSelector) 
    .WithRequired(Child.ParentSelector) 
    .Map(a => a.MapKey("ParentId")); 

с полностью эквивалент по определению

modelBuilder.Entity<Child>() 
    .HasRequired(Child.ParentSelector) 
    .WithMany(Parent.ChildrenSelector) 
    .Map(a => a.MapKey("ParentId")); 

таблицы базы данных и FK одинаковы, но загрузка не работает! Таким образом, либо рабочее решение случайно попадает в черный ход, либо есть ошибка в EF. В обоих случаях эта функция кажется мне проблематичной, и я бы просто использовал вспомогательные устройства public, чтобы избежать сюрпризов.

+0

Большое спасибо, Иван. Это действительно очень приятно и конструктивно! Вы не можете немного изменить определение модели, то есть заменить свой: modelBuilder.Entity () .HasRequired (Child.ParentSelector) .WithMany (Parent.ChildrenSelector) .Map (a => a.MapKey ("ParentId «)); и посмотрите, работает ли прокси-сервер? Разве эти два подхода не должны быть избыточными друг к другу? Есть ли причина, почему вы использовали Map, а не HasForeignKey? –

+0

'HasForeignKey' - это когда у вас есть явное поле в модели. например если класс 'Child' имеет' public int ParentId {get; задавать; } ', тогда вы будете использовать' HasForeignKey (c => c.ParentId) '. Хотя «MapKey» используется, когда у вас нет такого явного поля, и просто для указания имени столбца таблицы для этого поля. Это необязательно, если вы не включаете это, вы получите по умолчанию «Parent_Id». Конфигурация очень важна и отличается в зависимости от того, какие поля вы явно указываете в классах сущностей. –

+0

Правильная конфигурация имеет решающее значение для работы решения. Первоначально я не включал конфигурацию и получал точно такое же поведение, какое вы объясняете в сообщении. –

Смежные вопросы