При отладке EF-запросов это помогает посмотреть на все выполнение в целом и запустить вместе с отладчиком приложений. Повторяющиеся запросы, выглядящие так, являются торговой маркой отложенных вызовов загрузки. В приложениях MVC это обычно вызывается сериализатором, когда объекты отправляются клиенту. (Вместо моделей проецируемого вида)
Не видя реального кода и выполняемых запросов, используя следующий пример:
db.TableParent.Where(li => li.itemID == 16 && li.TableChild.TableXX != null && li.TableChild.companyId == company.id).ToList();
Если соответствующий контроллер MVC возвращает это Возвращаем к представлению коллекцию «Parents», сериализатор будет перебирать каждого родителя, смотреть ссылку на TableChild (вместе с любыми другими ссылками для детей, внуков и т. Д. c. Et c.) И когда он касается этих свойства, если DbContext еще не разрешил их, ссылки будут лениво загружаться по одному.
Простое решение в этом случае состоит в добавлении .Include(x => x.TableChild)
к запросу до .Where()
пункт. В долгосрочной перспективе было бы полезно смотреть на отправляющие модели представлений, а не на сущности, чтобы избежать неожиданных неожиданностей, подобных этой.
Обновление: GroupBy
не проходит через выражения Include
в EF. Вам нужно либо выполнить GroupBy
в памяти (дорого), либо использовать GroupBy
для определения идентификаторов родителей, а затем загрузить родителей с их загруженными отношениями. В качестве альтернативы, если вы можете проецировать ваши ViewModels в выражение запроса, вы сможете составлять модели представлений, не стремясь загрузить отношения.
Пример 1: Получить идентификаторы, а затем загрузить:
List<int> currentTableParentItemIds = db.TableParent
.Where(i => i.id == givenId)
.GroupBy(i => new { i.id1, i.id2})
.SelectMany(location => location
.OrderByDescending(i => i.order)
.Take(1))
.Select(i => i.id) // Assuming this gives you the ID of the records you want.
.ToList();
List<TableParentItem> currentTableParentItems = db.TableParent
.Include(i => i.TableChild)
.Include(i => i.TableXX)
.Where(i => currentParentItemIds.Contains(i.id))
.OrderBy(i => i.StartDate)
.ThenByDescending(i => i.id2)
.ToList();
Первый запрос выполняет группировку по, чтобы найти соответствующие идентификаторы строк. Второй выполняет нашу полную загрузку и сортировку, чтобы получить сущности и их родственников.
Пример 2: Если эта загрузка должна заполнить модель представления, то вам лучше снять загрузочную нагрузку Includes
и проецировать ваш вид моделируется непосредственно в выражении запроса, используя Select
или Automapper's ProjectTo
. Это может быть немного рефакторингом, если вы используете фабричные методы или такие для заполнения моделей представлений, так как они не проецируются через EF до SQL.
List<ParentItemViewModel> currentTableParentItems = db.TableParent
.Where(i => i.id == givenId)
.GroupBy(i => new { i.id1, i.id2})
.SelectMany(location => location
.OrderByDescending(i => i.order)
.Take(1))
.Select(i => new ParentItemViewModel
{
id = i.Id,
ChildName = i.TableChild.Name, // etc. Use whatever related tables needed. No need to eager load.
// ...
}).ToList();
Преимущество этого подхода заключается в том, что что он генерирует наиболее оптимальный SQL. Вместо того, чтобы возвращать все родительские столбцы и все столбцы связанных объектов, SQL, сгенерированный этим выражением, вернет только то, что необходимо для заполнения модели представления. Также нет риска отложенных загрузок до тех пор, пока вы не внедрите какие-либо объекты в модель представления. (Т. Е. Child = i.TableChild
= плохо)
Если код для возврата сущностей изолирован в чем-то вроде репозитория и возвращает что-то вроде IEnumerable<ParentTable>
, то для принятия оптимизаций, подобных примеру 2, можно облегчить принятие типа возврата IQueryable<ParentTable>
, чтобы потребители могли уточнить запросы EF для проецирования их конкретных c потребностей. В противном случае, вариант № 1 должен привести вас туда.