Цепочка к скомпилированному запросу теряет выигрыш в производительности - PullRequest
0 голосов
/ 08 ноября 2011

Я начал использовать скомпилированные запросы, чтобы повысить производительность некоторых часто выполняемых запросов linq to entity. В одном сценарии я только сводил запрос к его основной форме и предварительно компилировал его, а затем добавлял дополнительные предложения where на основе пользовательского ввода.

В данном конкретном случае я, похоже, теряю выигрыш в производительности от скомпилированных запросов. Может кто-нибудь объяснить, почему?

Вот пример того, что я делаю ...

IEnumerable<Task> tasks = compiledQuery.Invoke(context, userId); 

if(status != null)
{
    tasks = tasks.Where(x=x.Status == status);
}
if(category != null)
{
   tasks = tasks.Where(x=x.Category == category);
}

return tasks;

Ответы [ 3 ]

1 голос
/ 08 ноября 2011

Я думаю, что важно понимать, как работают скомпилированные запросы в EF.

Когда вы выполняете запрос, Entity Framework будет сопоставлять дерево выражений с помощью вашего файла сопоставления (EDMX или кода, в котором сначала определены ваши модели) с запросом SQL. Это может быть сложной и ресурсоемкой задачей.

Предварительная компиляция сохраняет результаты этой фазы отображения, поэтому при следующем нажатии на запрос уже будет доступен SQL, и ему нужно будет только установить текущие параметры.

Проблема в том, что предварительно скомпилированный запрос потеряет свою производительность, как только вы измените запрос. Допустим, у вас есть следующее:

IQueryable query = GetCompiledQuery(); // => db.Tasks.Where(t => t.Id == myId);
var notModifiedResult = query.ToList(); // Fast
int ModifiedResult = query.Count(); // Slow

С первым запросом вы получите все преимущества прекомпиляции, потому что EF уже сгенерировал для вас SQL и может выполнить его немедленно. Второй запрос потеряет прекомпиляцию, потому что он должен восстановить свой SQL.

Если вы сейчас выполните запрос на notModifiedResult, то это будет запрос Linq To Objects, поскольку вы уже выполнили свой SQL в базу данных и извлекли все элементы в памяти.

Однако вы можете объединить скомпилированные запросы (то есть использовать скомпилированный запрос в другом скомпилированном запросе).

Но для вашего кода потребуется ряд скомпилированных запросов: - По умолчанию - тот, где статус! = Ноль - та, где категория! = Ноль - Тот, где и статус и категория! = Ноль

1 голос
/ 08 ноября 2011

(Примечание: я целую вечность не работал над EF, а потом это было просто возня1004 *

Любые дальнейшие запросы должны выполняться внутри процесса .NET, а не в SQL.Все возможные результаты должны быть получены из базы данных и отфильтрованы локально.Попробуйте вместо этого:

IQueryable<Task> tasks = compiledQuery.Invoke(context, userId);

(Конечно, если это действительно так.)

0 голосов
/ 08 ноября 2011

Скомпилированный запрос не может быть изменен, могут быть изменены только параметры. На самом деле вы выполняете запрос и затем фильтруете результаты.

.Invoke(context, userId);  // returns all the results
.Where(....) // filters on that entire collection

Вы можете увидеть, есть ли умный способ переформулировать ваш запрос, чтобы параметры могли быть включены во всех случаях, но не оказали никакого влияния. Я не работал с скомпилированными запросами, извините за это, но работает ли это (используя -1 в качестве значения "ignore")?

// bunch of code to define the compiled query part, copied from [msdn][1]
(ctx, total) => from order in ctx.SalesOrderHeaders
                        where (total == -1 || order.TotalDue >= total)
                        select order);

В SQL вы делаете это, используя динамический sql или передавая значение по умолчанию (или ноль), которое указывает, что параметр следует игнорировать

select * from table t
where
    (@age    = 0     or t.age = @age) and
    (@weight is null or t.weight = @weight)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...