2011-11-08 33 views
0

我開始使用編譯查詢來增加一些通常執行的linq到實體查詢的性能。在一種情況下,我只是將查詢編譯成最基本的形式並預編譯,然後我根據用戶輸入添加附加的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; 

回答

1

我認爲這是很重要了解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執行到數據庫並獲取了內存中的所有元素。

然而,您可以鏈式編譯查詢(即在另一個編譯查詢中使用編譯查詢)。

但您的代碼將需要一系列編譯查詢:!!! - 默認 - 一個在那裏的地位= NULL - 一個類別爲空 - 一個其中兩個狀態和類別=空

1

(注:我沒有做過的年齡任何EF工作,然後它只是慢條斯理這僅僅是一個明智的猜測。 ,真)

這可能是罪魁禍首:

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

任何進一步的查詢將不得不在.NET過程中完成的,而不是SQL。所有可能的結果將不得不從數據庫中獲取並在本地過濾。試試這個:

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

(假設是有效的,當然)

0

編譯查詢無法改變,只有參數是可以改變的。你在這裏做的是實際運行查詢,然後篩選結果。

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

您可以看到是否有巧妙的方法來重新生成查詢,以便參數可以包含在所有情況下,但不會產生任何影響。我沒有編譯過的查詢,很抱歉,但這是否工作(使用-1作爲「忽略」值)?

// 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)