при первом использовании кода доступ к ассоциации не учитывает .Take (x) - PullRequest
1 голос
/ 07 июля 2011

2 объекта: Member и Comment
Member имеет ICollection<Comment> Comments

Всякий раз, когда я использую member.Comments.Take(x) EF создает запрос, который получает все комментарии из базы данных. Это должно быть так?
Это потому, что собственность - это ICollection? Есть ли способ заставить EF учитывать мой Take (x), или я должен реорганизовать свой код, чтобы использовать context.Comments.Where(c=>c.MemberId==member.Id).Take(x) и жить с ним?

Ответы [ 2 ]

3 голосов
/ 07 июля 2011

Как описано @J.Тихон, это как EF работает.При доступе к отложенному загруженному свойству EF всегда загружает всю коллекцию, и любое выражение Linq оценивается в загруженной коллекции.Если вы хотите избежать этого, вы должны использовать запрос, как вы описали, но результат запроса не будет загружен в ваше свойство навигации.Чтобы решить эту проблему, вы можете использовать явную загрузку вместо отложенной загрузки:

context.Entry(member)
       .Collection(m => m.Comments)
       .Query()
       .OrderBy(...) // Take requires some sorting
       .Take(2)
       .Load();

Это должно заполнить ваше свойство Comments двумя комментариями.

1 голос
/ 07 июля 2011

Прокси-классы, сгенерированные EF, обеспечивают отложенную загрузку только для свойств навигации, но не оценивают запросы. Как только вы получили доступ к свойству member.Comments, сущности Comment загружаются из базы данных, и ваш запрос применяется в памяти. Чтобы избежать этого, вы должны получить свои комментарии в запросе, который непосредственно выполняется на наборе объектов (как пример, который вы уже дали).

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

Вы уже описали способ справиться с этим, хотя это не очень красиво. Другим вариантом было бы как-то сказать EF частично загрузить свойство при выполнении исходного запроса для объекта Member. Я рассмотрю это, но уже могу вспомнить одну или две мысли, которые могут пойти не так с этим подходом.

Редактировать После некоторых исследований, проб и ошибок я не смог придумать другой подход, который мог бы быть выполнен непосредственно на DbSet<Member>, а не на DbSet<Comment>, и возвращает объект Member. Я могу использовать анонимный объект:

var query = from m in catalog.Members
            select new
            {
                Id = m.Id,
                Name = m.Name,
                Comments = m.Comments.Take(1)
            };

Который затем может быть переведен в объект-член в памяти, но, конечно, он не будет связан с контекстом в любом случае (= без отслеживания изменений). В приведенном выше примере запроса я не могу создать экземпляр Member вместо анонимного типа, потому что EF может создавать только не сложные типы (я полагаю, потому что контекст знает, что «Member» является сущностью).

...