TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.77K subscribers
Post #1916 1.37K
День 1570. #ЗаметкиНаПолях
Избегайте Распространения DbContext или IQueryable в Приложениях. Окончание
Начало

Когда вы везде передаёте IQueryable, вы позволяете логике доступа к данным распространяться по всей кодовой базе. Нет никакой инкапсуляции логики и правил доступа к данным. Приложение не следует принципу разделения ответственности, поскольку позволяет логике данных «протекать» в каждую часть системы.

Более того, код за пределами уровня данных может даже не знать, что он имеет дело с IQueryable, потому что IQueryable наследует IEnumerable. Методы могут работать с IEnumerable, ожидая поведения только в памяти, но на самом деле манипулируя деревом выражений IQueryable. Это может привести к неожиданным исключениям во время выполнения, особенно если результаты фильтруются с использованием кода, который EF не может преобразовать в SQL (например, пользовательской функции). Страдает понимание кода, а также отладка, устранение неполадок и тестирование. Место IQueryable - на уровне данных. В репозитории допустимо использование IQueryable, при условии, что он не является частью абстракции или публичного интерфейса типа.

Поскольку DbContext или DbSet<T> можно использовать для получения IQueryable в любое время, передача их за пределы уровня данных имеет такое же (негативное) влияние на разделение ответственности и инкапсуляцию, что и передача IQueryable<T>.

Какая есть альтернатива?
Во-первых, вы можно передавать дерево выражений в сервис данных и использовать его:
public IOrderRepository
{
List<Order> List(
Expression<Func<Order,bool>> filter);
}

Вы создаёте фильтр заранее, он исполняется в сервисе данных, а возвращаемый результат – данные в памяти.

Ещё лучше использовать паттерн Спецификация. В этом случае вместо передачи LINQ-выражения вы передаёте экземпляр спецификации, а выражение строится в ней:
public IOrderRepository
{
List<Order> List(
Specification<Order> spec);
}

В вызывающем коде вы определяете необходимую спецификацию как часть модели предметной области, а затем используете её в том месте, где вам нужны данные:
public class ShippedOrdersForUserSpec :
Specification<Order>
{
public ShippedOrdersForUserSpec(
string username)
{
Query.Where(o => !o.Canceled &&
o.CreatedBy == username &&
o.Shipped);
}
}

// в контроллере/на странице
var spec = new ShippedOrdersForUserSpec(username);
var viewModel = await _repo.ListAsync(spec);

Таким образом запрос по-прежнему выполняется в базе данных, но IQueryable больше не используется нигде за пределами реализации репозитория. Вы можете посмотреть на использование этого паттерна в примере eShopOnWeb (см. папку /src/ApplicationCore/Specifications/).

Итого
Распространение IQueryable по всему приложению позволяет расширять запросы к БД из любого места. Это и хорошо, и плохо, и соответствует определению антипаттерна, поскольку сначала кажется чем-то стоящим, но на практике приводит к проблемам. Решение состоит не в том, чтобы получать нефильтрованные данные и затем фильтровать их на сервере приложений, а в том, чтобы идентифицировать конкретные запросы, которые потребуются отдельным страницам или конечным точкам, и определять их как первоклассные абстракции в модели предметной области приложения. Паттерны Спецификация и Репозиторий прекрасно подходят для достижения такого дизайна и обеспечивают гораздо более удобный и тестируемый результат.

Источник: https://ardalis.com/avoid-dbcontext-iqueryable-proliferation/
  • 👍 13
More from @netdeveloperdiary
  1. Oct 11, 2026День 2811. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  2. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
  3. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
  4. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  5. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  6. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →