EF 4.1 загрузка фильтрованных дочерних коллекций, не работающих для многих-ко-многим

Я смотрел Применение фильтров при явной загрузке связанных объектов и не мог заставить его работать для отношения "многие ко многим".

Я создал простую модель: Model

Краткое описание:
A Student может принимать много Courses, а Course может иметь много Students.
A Student может сделать много Presentation, но a Presentation может быть сделано только одним Student.
Итак, у нас есть отношение "много-ко-многим" между Students и Courses, а также отношение "один ко многим" между Student и Presentations.

Я также добавил один Student, один Course и один Presentation, связанные друг с другом.

Вот код, который я запускаю:

class Program
{
    static void Main()
    {
        using (var context = new SportsModelContainer())
        {
            context.Configuration.LazyLoadingEnabled = false;
            context.Configuration.ProxyCreationEnabled = false;

            Student student = context.Students.Find(1);

            context.
                Entry(student).
                Collection(s => s.Presentations).
                Query().
                Where(p => p.Id == 1).
                Load(); 

            context.
                Entry(student).
                Collection(s => s.Courses).
                Query().
                Where(c => c.Id == 1).
                Load();

            // Trying to run Load without calling Query() first
            context.Entry(student).Collection(s => s.Courses).Load();
        }
    }
}

После загрузки презентаций я вижу, что счетчик для Presentations изменился с 0 на 1: After loading presentations. Однако после выполнения этого же с Courses ничего не меняется: After attempting to load courses

Поэтому я пытаюсь загрузить курсы без вызова Query и работает как ожидалось: Courses loaded

(Я удалил предложение Where, чтобы еще больше выделить точку - последние две попытки загрузки различаются только по вызову "Query()"

Теперь единственное различие, которое я вижу, состоит в том, что одна связь является "один ко многим", а другая - "ко многим". Это ошибка EF, или я чего-то не хватает?

И btw, я проверил вызовы SQL для двух последних попыток Course -loading, и они на 100% идентичны, поэтому кажется, что EF не заполняет коллекцию.

Ответ 1

Я мог бы воспроизвести именно то поведение, которое вы описываете. То, что я получил, это:

context.Entry(student)
       .Collection(s => s.Courses)
       .Query()
       .Include(c => c.Students)
       .Where(c => c.Id == 1)
       .Load();

Я не знаю, почему мы должны быть вынуждены также загружать другую сторону отношения "многие ко многим" (Include(...)), когда мы хотим загрузить только одну коллекцию. Для меня это действительно похоже на ошибку, если я не пропустил скрытую причину этого требования, которое где-то зарегистрировано или нет.

Edit

Другой результат: ваш исходный запрос (без включения)...

context.Entry(student)
       .Collection(s => s.Courses)
       .Query()
       .Where(c => c.Id == 1)
       .Load();

... фактически загружает курсы в DbContext как...

var localCollection = context.Courses.Local;

... показывает. Курс с Id 1 действительно находится в этой коллекции, что означает: загружается в контекст. Но это не в дочерней коллекции студенческого объекта.

Изменить 2

Возможно, это не ошибка.

Прежде всего: мы используем здесь две разные версии Load:

DbCollectionEntry<TEntity, TElement>.Load()

Intellisense говорит:

Загружает коллекцию объектов из базы данных. Обратите внимание, что объекты, которые уже существуют в контексте, не перезаписаны значениями из базы данных.

Для другой версии (метод расширения IQueryable)...

DbExtensions.Load(this IQueryable source);

... Intellisense говорит:

Перечисляет запрос так, что для серверные запросы, такие как запросы System.Data.Entity.DbSet, System.Data.Objects.ObjectSet, System.Data.Objects.ObjectQuery, и другие результаты запроса будут загружены в связанные System.Data.Entity.DbContext, System.Data.Objects.ObjectContext или другой кэш на клиенте. Это эквивалентно вызову ToList, а затем отбросив список без накладные расходы на список.

Итак, в этой версии не гарантируется, что дочерняя коллекция заполняется, только чтобы объекты загружались в контекст.

Остается вопрос: зачем получает коллекцию Presentations, а не коллекцию Courses. И я думаю, что ответ таков: из-за Отношения > .

Сектор отношений - это функция в EF, которая автоматически фиксирует отношения между объектами, которые находятся в контексте или которые просто загружаются в контекст. Но это не происходит для всех типов отношений. Это происходит, только если кратность 0 или 1 на одном конце.

В нашем примере это означает: когда мы загружаем Presentations в контекст (по нашему отфильтрованному явному запросу), EF также загружает внешний ключ Presentation entites в объект Student - "прозрачно", что означает, что независимо от того, будет ли FK выставлен как свойство в модели нет. Этот загруженный FK позволяет EF распознавать, что загруженный Presentations принадлежит сущности Student, которая уже находится в контексте.

Но это не так для коллекции Courses. Курс не имеет внешнего ключа для объекта Student. Между ними есть много-много-соединительная таблица. Таким образом, при загрузке Courses EF не распознает, что эти курсы принадлежат Student, который находится в контексте, и поэтому не фиксирует коллекцию навигации в объекте Student.

EF делает это автоматическое исправление только для ссылок (а не коллекций) по соображениям производительности:

Чтобы зафиксировать взаимосвязь, EF прозрачно переписывает запрос, чтобы информация о взаимоотношениях для всех отношений который имеет кратность 0..1 or1 on другой конец; другими словами свойства навигации, которые являются сущностью Справка. Если предприятие имеет отношения с множественностью больше 1, EF не вернет информация о взаимоотношениях, поскольку она может быть удачным по производительности и по сравнению с привлечение одного иностранца вместе с остальная часть записи. приведение информация о взаимоотношениях означает получение всех внешние ключи, которые были записаны.

Цитата со страницы 128 Zeeshan Hirani в подробном руководстве к EF.

Он основан на EF 4 и ObjectContext, но я думаю, что это все еще действует в EF 4.1, поскольку DbContext в основном представляет собой оболочку вокруг ObjectContext.

К сожалению, довольно сложный материал, который следует учитывать при использовании Load.

И еще один Edit

Итак, что мы можем сделать, когда хотим явно загрузить одну отфильтрованную сторону отношения "многие ко многим"? Возможно, только это:

student.Courses = context.Entry(student)
       .Collection(s => s.Courses)
       .Query()
       .Where(c => c.Id == 1)
       .ToList();