Ответ 1
Вы можете запросить необходимые объекты по
Context.Configuration.LazyLoadingEnabled = false;
// Or: Context.Configuration.ProxyCreationEnabled = false;
var buses = Context.Busses.Where(b => b.IsDriving)
.Select(b => new
{
b,
Passengers = b.Passengers
.Where(p => p.Awake)
})
.AsEnumerable()
.Select(x => x.b)
.ToList();
Здесь происходит то, что вы сначала выбираете ведущие автобусы и пробуждаете пассажиров из базы данных. Затем AsEnumerable()
переключается с LINQ на Entities на LINQ на объекты, что означает, что автобусы и пассажиры будут материализованы и затем обработаны в памяти. Это важно, потому что без него EF только материализует окончательную проекцию, Select(x => xb)
, а не пассажиров.
Теперь в EF есть исправление отношений между объектами, которое заботится об установке всех ассоциаций между объектами, которые материализуются в контексте. Это означает, что для каждого Bus
теперь загружены только его активные пассажиры.
Когда вы получаете коллекцию автобусов с помощью ToList
вас есть автобусы с ToList
пассажирами, и вы можете сопоставить их с AutoMapper.
Это работает только тогда, когда отложенная загрузка отключена. В противном случае EF будет лениво загружать всех пассажиров для каждого автобуса при обращении к пассажирам во время преобразования в DTO.
Есть два способа отключить отложенную загрузку. Отключение LazyLoadingEnabled
повторно активирует LazyLoadingEnabled
загрузку, когда она снова включается. Отключение ProxyCreationEnabled
создаст сущности, которые не способны выполнять ProxyCreationEnabled
загрузку, поэтому они не начнут ProxyCreationEnabled
загрузку после ProxyCreationEnabled
включения ProxyCreationEnabled
. Это может быть лучшим выбором, когда контекст живет дольше, чем этот единственный запрос.
Но... многие ко многим
Как уже говорилось, этот обходной путь зависит от исправления отношений. Однако, как объяснил здесь Слаума, исправление отношений не работает с ассоциациями "многие ко многим". Если Bus
- Passenger
много-ко-многим, единственное, что вы можете сделать, это исправить это самостоятельно:
Context.Configuration.LazyLoadingEnabled = false;
// Or: Context.Configuration.ProxyCreationEnabled = false;
var bTemp = Context.Busses.Where(b => b.IsDriving)
.Select(b => new
{
b,
Passengers = b.Passengers
.Where(p => p.Awake)
})
.ToList();
foreach(x in bTemp)
{
x.b.Pasengers = x.Passengers;
}
var busses = bTemp.Select(x => x.b).ToList();
... и все это становится еще менее привлекательным.
Сторонние инструменты
Существует библиотека EntityFramework.DynamicFilters, которая делает это намного проще. Это позволяет вам определять глобальные фильтры для сущностей, которые впоследствии будут применяться каждый раз, когда сущность запрашивается. В вашем случае это может выглядеть так:
modelBuilder.Filter("Awake", (Person p) => p.Awake, true);
Теперь, если вы делаете...
Context.Busses.Where(b => b.IsDriving)
.Include(b => b.People)
... вы увидите, что фильтр применяется к включенной коллекции.
Вы также можете включить/отключить фильтры, чтобы вы могли контролировать, когда они применяются. Я думаю, что это очень аккуратная библиотека.
Есть похожая библиотека от создателя AutoMapper: EntityFramework.Filters
Ядро Entity Framework
Начиная с версии 2.0.0, EF-core имеет фильтры запросов на уровне модели. Хотя это отличное дополнение к его функциям, до сих пор существует ограничение, заключающееся в том, что его нельзя применять к свойствам навигации, а только к корневому объекту запроса. Надеемся, что в более поздней версии эти фильтры получат более широкое использование.
Отфильтрованные включает в себя давний запрос функции. EF-core проблема может быть найдена здесь.