Linq2SQL - Как вы лениво загружаете вложенные списки?
-
16-09-2019 - |
Вопрос
Как вы лениво загружаете вложенные списки без фактического выполнения запроса?Используя очень простой пример, скажем, у меня есть:
class CityBlock {
IList<Building> BuildingsOnBlock;
Person BlockOwner;
}
class Building {
IList<Floor> FloorsInBuilding;
}
class Floor {
IList<Cubicle> EmployeeCubicles;
}
Class Cubicle {
System.Guid CubicleID;
Person CubicleOccupant;
}
И затем в моем слое репозитория у меня есть следующий метод:
GetCityBlocks()
И затем на уровне сервиса у меня будет GetCityBlocksByOwner , где я использую метод расширения, чтобы получить городские кварталы, принадлежащие определенному человеку, скажем, нам просто нужны блоки Guido:
GetCityBlocks().ForOwner("Guido")
Если мы сделаем .ToList() в репозитории, он будет выполнять запросы - это будет нелепо, поскольку мы не знаем, чьи блоки мы получаем на этом уровне.Итак, вопрос в том, как нам сделать это эффективно?
Давайте предположим, что есть 50000 владельцев блоков и около 1000000 городских кварталов, загрузить их все - не вариант.Использование IQueryables не будет работать из-за вложенности (без экстремального взлома, по крайней мере, насколько мне известно).Кроме того, если я попытаюсь использовать что-то вроде LazyList Роба Конери, то у нас, по сути, произойдет утечка из нашего DAL в наши доменные модели, что может быть очень, очень плохо в будущем.
Итак, как мне сделать это правильно?
- Это вопрос определения правильного контекста?Если да, то сделали бы мы это на уровне хранилища или На уровне сервиса?
- Должен ли я наполовину объединить уровень сервиса и уровень моего хранилища, чтобы получить очень специфические методы обслуживания?
- Или я что-то совсем упускаю?(все еще относительно новый для Linq2SQL вещь, которая постепенно прекращается в любом случае, так что ...)
Редактировать: В шаблоне репозитория мы в настоящее время сопоставляем объекты нашего домена, так что это будет выглядеть примерно так:
public IQueryable<CityBlock> GetCityBlocks(){
var results = from o in db.city_blocks
let buildings = GetBuildingsOnBlock(o.block_id)
select new CityBlock {
BuildingsOnBlock = buildings,
BlockOwner = o.block_owner
};
return results;
}
Чтобы это сработало, мы должны были бы сделать так, чтобы здания получили a .ToList() , если только мы не сделаем это фактическое поле в объекте CityBlock IQueryable - что кажется неправильным, потому что кажется, что слишком много полномочий будет предоставлено любому, кто обращается к CityBlock.Поле BuildingsOnBlock.Является ли это сопоставление с объектами нашего домена чем-то, что мы, возможно, должны сделать на уровне сервиса?
Решение
Вы делаете это, возвращая IQueryables вместо ILists .
ToList() вызывает немедленное выполнение запросов, поскольку должно быть выполнено преобразование из IQueryable в IList.
Пока вы возвращаете IQueryables, отложенная загрузка должна откладывать выполнение до тех пор, пока данные действительно не понадобятся, т. Е.когда вызывается ToList().
На данный момент я не могу найти ссылку, но, насколько я понимаю, если вы сделаете это таким образом, linq to sql получит возможность оптимизировать SQL, который он отправляет на сервер.Другими словами, в конечном итоге он прочитает эти записи:
GetCityBlocks().ForOwner("Guido")
вместо этих записей:
GetCityBlocks()
Другие советы
Вы могли бы попробовать другой подход к отображению объектов вашего домена, чтобы заставить его работать.Проблема в том, что независимо от того, что вы делаете (если только вы не измените свои списки на IQueryables в объектах вашего домена), вы в конечном итоге будете использовать ToList() во время сопоставления.
Другой способ - позволить Linq2SQL выполнять сопоставление с вашими POCOs, создавая пользовательский datacontext и не используя конструктор для сопоставления :), таким образом, вы сохраняете свою модель домена чистой и позволяете Linq2SQL заполнять зависимости в нужное время.Обратите внимание, что переход на этот маршрут сопряжен со своими собственными проблемами, но это можно сделать.
Вот ссылка, которая поможет вам начать работу по этому маршруту