Здравствуйте, Evgeny.Panasyuk, Вы писали:
Большой тред, пропустил ветку.
EP>Что конкретноАвтор: Evgeny.Panasyuk
Дата: 19.04.15
имеется в виду?
Речь про 1 и 2.
>1) Запросы статические по сути, но сконструированные из нескольких частей, возможно даже в разных функциях.
>2) Статические запросы построенные через динамическую композицию — статические части запроса комбинируются разными способами в зависимости от runtime условий (то есть расширение пункта 1).
Да, именно этот вариант мне и не понятен.
Из того что я понял, предлагается представить AST-запроса в типе и производить вычисления в typelevel. Тогда вопрос не к макросам, а к системе типов. Ни в C#, ни в Nemerle typelevel вычислений не предвидится, потому я и хотел узнать у IT, как он видит linq в compile time, и как ему могла помочь Nitra.
EP>Вот здесьАвтор: Evgeny.Panasyuk
Дата: 14.04.15
вариант решения.
auto q1 = context.Employees;
auto q2 = conditional(!IsNullOrEmpty(lastName), q1.Where(e.LastName = lastName));
auto q3 = conditional(!IsNullOrEmpty(firstName), q2.Where(e.FirstName= firstName));
auto q4 = conditional(HasValue(employmentDateFrom), q3.Where(e.EmploymentDate >= employmentDateFrom.Value));
auto q5 = conditional(HasValue(employmentDateTo)), q4.Where(e.EmploymentDate <= employmentDateTo.Value));
//далее в Presentation Layer (в другом месте)
auto list = await q5.Take(pageSize).Select(/*projection*/).ToListAsync();
>При попытке выполнить этот запрос, будет произведено 4 runtime проверки, в результате чего будет выбрана одна из 16 сгенерированных в compile-time версий запроса (причём которые могут кардинально отличаться друг от друга).
>Если не нравится экспоненциальное количество запросов в исполняемом файле, то можно добавить динамики. Да, при этом запрос будет частично строится в runtime (с возможным кэшированием), но при этом не будет дорогой runtime reflection — в ней тут нет принципиальной необходимости, достаточно compile-time reflection.
Для наглядности заменил условия
auto q2 = conditional(searchParams.HasFilterByLastName, q1.Where(e.LastName == searchParams.LastName));
auto q3 = conditional(searchParams.HasFilterByFirstName, q2.Where(e.FirstName == searchParams.FirstName));
Тут, со слов IT, мы на каждый qN.Where(e.SomeField == someValue), где someValue может быть null, получаем еще по 2 варианта sql-запроса (с параметром и с 'is null'). Соответственно, число генерируемых запросов растет по экспоненте не только от количества условий, но и от числа нуллабельных переменных.
>4) Стирание полного типа запроса — это позволяет частично отличающимся запросам иметь одинаковый тип. Степень отличия контролируется тем, насколько стёрт тип.
>Насколько я вижу, вариант 4) тоже далеко не всегда необходим. Причём каких-то принципиальных предпосылок/аргументов к его необходимости в этом топике я не увидел.
Всегда или нет, но я видел множество ярлычков с какой-то часто используемой логикой вроде:
public IQuerable<Employe> ActiveEmployees { return _context.Employees.Where(e => e.IsActive); }
Итог:
1. нужен язык, поддерживающий вычисления в typelevel
2. размер генерируемого кода растет экспоненциально от числа условий и числа нуллабельных переменных (это только пример с Where разобрали)
3. при изменение запроса, весь зависимый код должен быть перекомпилирован
4. создание публичного интерфейса (типо паттерна "репозиторий") затруднено
5. непонятно, при чем здесь макросы