EF 4.1 загрузка отфильтрованных дочерних коллекций не работает для многих ко многим - PullRequest
13 голосов
/ 26 мая 2011

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

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

Краткое описание:
Student может иметь много Courses, а Course может иметь много Students.
Student может сделать много Presentation, но 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 или я что-то упустил?

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

1 Ответ

18 голосов
/ 26 мая 2011

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

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

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

Редактировать

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

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

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

var localCollection = context.Courses.Local;

... шоу.Курс с идентификатором 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.И я думаю, что ответ таков: из-за Spansion Span .

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

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

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

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

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

Цитата со страницы 128 из Подробное руководство Зеешана Хирани по EF .

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

К сожалению, при использовании Load.

необходимо учитывать довольно сложные вещи.

И еще один Правка

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

student.Courses = context.Entry(student)
       .Collection(s => s.Courses)
       .Query()
       .Where(c => c.Id == 1)
       .ToList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...