Pull to refresh

Унифицируем поведение LINQ to IEnumerable и LINQ to IQueriable в части работы с null значениями. Часть вторая. Своя реализация IQueryProvider

.NET *C# *
В комментариях к первой части мне справедливо сделали замечание, что я обещал унификацию IEnumerable и IQueryable, а сам спрятал их за самописным интерфейсом типа репозитория. В этой статье я постараюсь исправится и дать пример что же делать, если мы хотим работать с LINQ напрямую. Для этого я предложу собственную реализацию интерфейса IQueryProvider.

GitHub
Nuget

Много кода
Total votes 12: ↑11 and ↓1 +10
Views 8.7K
Comments 3

Принципы работы IQueryable и LINQ-провайдеров данных

.NET *C# *
Sandbox
Средства LINQ позволяют .Net-разработчикам единообразно работать как с коллекциями объектов в памяти, так и с объектами, хранящимися в базе данных или ином удаленном источнике. Например, для запроса десяти красных яблок из списка в памяти и из БД средствами Entity Framework мы можем использовать абсолютно идентичный код:

List<Apple> appleList; 
DbSet<Apple> appleDbSet;
var applesFromList = appleList.Where(apple => apple.Color == “red”).Take(10);
var applesFromDb = appleDbSet.Where(apple => apple.Color == “red”).Take(10);

Однако, выполняются эти запросы по-разному. В первом случае при перечислении результата с помощью foreach яблоки будут отфильтрованы с помощью заданного предиката, после чего будут взяты первые 10 из них. Во втором случае синтаксическое дерево с выражением запроса будет передано специальному LINQ-провайдеру, который транслирует его в SQL-запрос к базе данных и выполнит, после чего сформирует для 10 найденных записей объекты С# и вернет их. Обеспечить такое поведение позволяет интерфейс IQueryable<T>, предназначенный для создания LINQ-провайдеров к внешним источникам данных. Ниже мы попробуем разобраться с принципами организации и использования этого интерфейса.
Читать дальше →
Total votes 26: ↑26 and ↓0 +26
Views 63K
Comments 3

Трансляция запросов в SQL с использованием LinqToSql в тестах

Mindbox corporate blog .NET *C# *
Мы уже несколько лет делаем наш продукт автоматизации маркетинга, и пилить фичи с высокой скоростью нам помогает CI, а точнее — большое количество автоматических тестов.

В продукте примерно 700 000 строк кода со всеми кастомизациями, и на это всё мы имеем около 7 000 тестов, и их количество постоянно растёт. За счет них мы не боимся совершать большие рефакторинги, затрагивающие многие части системы. Но, к сожалению, тесты не панацея. Каких-то тестов может не быть, какие-то тесты могут оказаться слишком дорогими, а какие-то ситуации не воспроизводятся в тестовой среде.

Практически каждая транзакция в нашей системе связана с работой с MS SQL с использованием LinqToSql. Да, технология старенькая, но мигрировать с неё нам довольно сложно, и по бизнесу она нас вполне устраивает. Более того, как я уже писал раньше, у нас даже есть свой форк LinqToSql, где мы чуть-чуть чиним его баги и добавляем кое-какой функциональности.

Для того, чтобы делать запросы к БД, используя LinqToSql, нужно использовать интерфейс IQueryable. В момент получения Enumerator’а или выполнения Execute у QueryProvider’а построенное дерево выражений с помощью Extension-методов к IQueryable транслируется в SQL, который и выполняется на SQL Server.

Так как наша бизнес-логика сильно завязана на сущностях в базе данных, наши тесты много работают с базой данных. Однако в 95% тестов мы не используем реальную базу, так как это очень дорого по времени, а довольствуемся InMemoryDatabase. Она является частью нашей тестовой инфраструктуры, о которой можно написать отдельную статью, и на самом деле представляет из себя просто Dictionary<Type, List> для каждого существующего типа сущности. В тестах наш UnitOfWork прозрачно работает с такой базой, давая доступ к EnumerableQueryable, который просто получить из любого IEnumerable, вызвав у него AsQueryable().

Покажу пример теста для понимания происходящего:
Читать дальше →
Total votes 9: ↑9 and ↓0 +9
Views 4.7K
Comments 1

Enumerable: How to yield a business value

High performance *.NET *C# *ООP *Microservices *
This article is a brief explanation about how using a common language keywords might have an influence on the budget of IT-infrastructure of a project or help to achieve some limitations/restrictions of hosting infrastructure and, moreover, will be a good sing of the quality and maturity of the source code.
Read more →
Total votes 12: ↑11 and ↓1 +10
Views 1.8K
Comments 0

IQueryable порождает сильную связанность

.NET *C# *
Translation

Время от времени я встречаю людей, пытающихся выразить API в терминах IQueryable<T>. Почти всегда это плохая идея. В этой статье я объясню почему. Вкратце, IQueryable<T> — это один из лучших примеров заголовочного интерфейса (Header Interface), предлагаемых платформой .NET. Его почти невозможно реализовать полностью.


Эта статья о проблемах реализации API на основе интерфейса IQueryable<T>. Это не претензия к интерфейсу как таковому. Кроме этого, это не претензия к замечательным методам LINQ, доступным для интерфейса IEnumerable<T>.

Можно сказать, что IQueryable<T> — это одно сплошное нарушение принципа подстановки Лисков. Я буду использовать закон Постела, чтобы объяснить почему это так.


Принцип устойчивости, также известен как закон Постела в честь Джона Постела: «Будь либерален к тому, что принимаешь, и консервативен к тому, что отсылаешь (Be liberal in what you accept, and conservative in what you send)».
Читать дальше →
Total votes 23: ↑22 and ↓1 +21
Views 8.5K
Comments 38

Транслируй меня полностью

.NET *Designing and refactoring *C# *


Вы когда-нибудь работали с Entity Framework или другим ORM и получали NotSupportedException? Многие люди получали:


InvalidOperationException: Error generated for warning 'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning: The LINQ expression could not be translated and will be evaluated locally.'

Марк Симан твердо убежден, что, за одним исключением, все существующие реализации нарушают LSP. Он даже готов отправить бесплатную копию своей книги первому читателю, который укажет ему на реальную, общедоступную реализацию IQueryable<T>, которая может принять любое выражение и не выбросить исключение. За девять лет книга так и не нашла своего обладателя:)


  • Hi Mark,
    I am writing a blog post that refers to your artticle. I am wondering if you have ever sent a free copy of your book to someone. Presumably not:)
  • Hi Maxim
    That’s right: I haven’t.
    Regards
    Mark Seemann

В поддержку этой точки зрения можно привести и другие аргументы. Например, ToListAsync вообще отсутствует в наборе методов расширения из коробки. Вместо этого он определен в пакетах конкретных ORM. Значит ли это, что не стоит раскрывать IQueryable<T> в публичных API? Я думаю, что ответ на этот вопрос — «зависит».

Читать дальше →
Total votes 5: ↑5 and ↓0 +5
Views 5.5K
Comments 27