Использование свойств навигации Entity Framework без создания большого количества запросов (избегая N+1)
Я использовал Entity Framework Profiler для проверки доступа к данным в проекте MVC и перебрал несколько страниц, где я делаю гораздо больше запросов к базе данных из-за проблем N+1.
Вот простой пример, чтобы показать мою проблему:
var club = this.ActiveClub; // ActiveClub uses code similar to context.Clubs.First()
var members = club.Members.ToList();
return View("MembersWithAddress", members);
Представление перебирает членов и затем следует свойству навигатора для каждого члена, чтобы также показать их адрес. Каждый из запросов адреса приводит к дополнительному запросу БД.
Одним из способов решения этой проблемы было бы использование Include, чтобы убедиться, что дополнительные таблицы, которые мне нужны, запрашиваются заранее. Тем не менее, я, кажется, могу сделать это только с помощью ObjectSet of Clubs, прикрепленной непосредственно к контексту. В этом случае свойство ActiveClub совместно используется многими контроллерами, и я не всегда хочу запрашивать таблицу членов и адресов заранее.
Я хотел бы иметь возможность использовать что-то вроде:
var members = club.Members.Include("Address").ToList();
Но Members - это EntityCollection, в котором нет метода Include.
Есть ли способ принудительно загрузить элемент EntityCollection и попросить EF также загрузить их адреса?
Или, таким образом, использовать свойства навигации EntityCollection на объекте, просто плохая идея; и вы должны знать, что вы загружаете, когда вы получаете это из контекста?
1 ответ
Если ваши лица наследуют от EntityObject
попробуйте использовать это:
var members = club.Members.CreateSourceQuery()
.Include("Address")
.ToList();
Если вы используете POCO с ленивыми загрузочными прокси, попробуйте использовать это:
var members = ((EntityCollection<Club>)club.Members).CreateSourceQuery()
.Include("Address")
.ToList();
Очевидно, что вторая версия не очень хороша, потому что POCO используются для удаления зависимости от EF, но теперь вам нужно преобразовать коллекцию в класс EF. Другая проблема заключается в том, что запрос будет выполнен дважды. Ленивая загрузка сработает для Members
один раз для доступа к свойству, а затем второй запрос будет выполнен при вызове ToList
, Эту проблему можно решить, отключив отложенную загрузку перед выполнением запроса.
Когда ты сказал ActiveClub
Я считаю, что это означает что-то вроде этого свойства в базовом контроллере, используемом в производных контроллерах. В таком случае вы все равно можете использовать другой код в другом контроллере для заполнения свойства.