Информация об изменениях

Сообщение Re[13]: EntityFramework - тормоз от 14.04.2015 10:39

Изменено 14.04.2015 11:11 Evgeny.Panasyuk

Здравствуйте, gandjustas, Вы писали:

G>Делаем из него эффективный запрос на C#:

G>
G>var query = context.Employees;
G>if(!string.IsNullOrEmpty(lastName)) query = query.Where(e => e.LastName = lastName);
G>if(!string.IsNullOrEmpty(firstName)) query = query.Where(e => e.FirstName = firstName);
G>if(employmentDateFrom.HasValue) query = query.Where(e => e.EmploymentDate >= employmentDateFrom.Value);
G>if(employmentDateTo.HasValue) query = query.Where(e => e.EmploymentDate <= employmentDateTo.Value);

G>//далее в Presentation Layer (в другом классе и другой сборке)
G>var list = await query.Take(pageSize).Select(e => new {/*projection*/}).ToListAsync();
G>

G>Теперь покажи как сделать "инстумент, задающий эту же самую склейку, но автоматически на этапе компиляции".
G>Естественно все параметры зависят от ввода пользователя и невозможно их вычислить на этапе компиляции.

Один из возможных вариантов:
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.

G>Разделение на слои тоже является обязательным.


Не проблема — достаточно передать q5 с его типом в другой слой.

Если же хочется больше динамики (например q5 генерируется в отдельной динамической библиотеке и нужно иметь возможность изменять запрос без переборки всех зависимых компонентов) — то можно частично стереть информацию о типе (type erasure), но тогда как минимум появятся косвенные вызовы, а максимум — построение части запроса в runtime (но опять же, без runtime reflection).

G>А проекция в PL может видоизменить запрос, что появятся join_ы.


Да, это не помеха — в перелагаемом варианте запрос строится в самый последний момент, когда известны все runtime условия, а не склеивается из отдельных кусочков после каждой проверки.
Re[13]: EntityFramework - тормоз
Здравствуйте, gandjustas, Вы писали:

G>Делаем из него эффективный запрос на C#:

G>
G>var query = context.Employees;
G>if(!string.IsNullOrEmpty(lastName)) query = query.Where(e => e.LastName = lastName);
G>if(!string.IsNullOrEmpty(firstName)) query = query.Where(e => e.FirstName = firstName);
G>if(employmentDateFrom.HasValue) query = query.Where(e => e.EmploymentDate >= employmentDateFrom.Value);
G>if(employmentDateTo.HasValue) query = query.Where(e => e.EmploymentDate <= employmentDateTo.Value);

G>//далее в Presentation Layer (в другом классе и другой сборке)
G>var list = await query.Take(pageSize).Select(e => new {/*projection*/}).ToListAsync();
G>

G>Теперь покажи как сделать "инстумент, задающий эту же самую склейку, но автоматически на этапе компиляции".
G>Естественно все параметры зависят от ввода пользователя и невозможно их вычислить на этапе компиляции.

Один из возможных вариантов:
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.

G>Разделение на слои тоже является обязательным.


Не проблема — достаточно передать q5 с его типом в другой слой.

Если же хочется больше динамики (например q5 генерируется в отдельной динамической библиотеке и нужно иметь возможность изменять запрос без переборки всех зависимых компонентов) — то можно частично стереть информацию о типе (type erasure), но тогда как минимум появятся косвенные вызовы, а максимум — построение части запроса в runtime (но опять же, без runtime reflection).

G>А проекция в PL может видоизменить запрос, что появятся join_ы.


Да, это не помеха — в предлагаемом варианте запрос строится в самый последний момент, когда известны все runtime условия, а не склеивается из отдельных кусочков после каждой проверки.