2011-02-28 103 views
1

聲明:我幾乎沒有linq的使用經驗。需要幫助優化Linq環路

我的工作之一是維護一個電子商務網站。昨天,我們的一位客戶開始抱怨當他們嘗試爲Google創建Feed文件時發生超時。事實證明,如果用戶有超過9,000個項目需要放入其供稿文件,我們的代碼至少需要一分鐘才能執行。

我無法通過運行調試程序找到問題的根源,所以我啓動了一個分析器(ANTS)並讓它執行它的操作。它找到了我們問題的根源,一個包含一些linq代碼的foreach循環。這裏是代碼:

var productMappings = GoogleProductMappingAccess.GetGoogleProductMappingsByID(context, productID); 
List<google_Category> retCats = new List<google_Category>(numCategories); 
int added = 0; 

//this line was flagged by the profiler as taking 48.5% of total run time 
foreach (google_ProductMapping pm in (from pm in productMappings orderby pm.MappingType descending select pm)) 
{ 
    if (pm.GoogleCategoryId.HasValue && pm.GoogleCategoryId > 0) 
    { 
     //this line was flagged as 36% of the total time 
     retCats.Add(pm.google_Category); 
    } 

    else if (pm.GoogleCategoryMappingId.HasValue && pm.GoogleCategoryMappingId > 0) 
    { 
     retCats.Add(pm.google_CategoryMapping.google_Category); 
    } 
    else 
    { 
     continue; 
    } 

    if (++added >= numCategories) 
    { 
     break; 
    } 
} 

做任何你更有經驗的開發者有什麼想法嗎?我在嘗試用sql替換所有的linq,但我不確定這是否是這裏最好的操作過程(如果它是用linq編寫的,必須有它的原因)。

+0

不要在循環中執行linq。將結果存儲在一個變量中並使用它。 – madmik3 2011-02-28 23:15:45

+0

快速修復DataContext.CommandTimeout。 – 2011-03-01 00:45:26

+0

可能不必這樣做。如果可能的話儘量避免它 – CountMurphy 2011-03-01 00:48:11

回答

2

如果你可以過濾掉你不想反正你的查詢應該是更快的結果 - 你正在使用orderby因此所有這些結果使用起來處理您的查詢,因爲他們都必須進行評估:

productMappings.Where(pm => (pm.GoogleCategoryMappingId.HasValue 
           && pm.GoogleCategoryMappingId > 0) 
           ||(pm.GoogleCategoryMappingId.HasValue && 
           pm.GoogleCategoryMappingId > 0) 
        ) 
       .OrderBy(...) 

此外,您應限制查詢返回的結果數量,因爲您只使用最多numCategories。因此,添加一個

.Take(numCategories) 

到您的查詢,而不是在foreach循環內檢查。

+0

我要試一試,讓你知道會發生什麼 – CountMurphy 2011-02-28 23:25:42

+0

增加where子句將時間從48%提高到78%。採取似乎也沒有做太多。 – CountMurphy 2011-03-01 00:10:15

+0

那張桌子有多大? 「MappingType」(以及where子句中的其他列)是否有適當的索引? – BrokenGlass 2011-03-01 02:13:50

0

不知道你的數據庫模式,這真的很難說。幾個想法:

1)通過數據庫引擎優化顧問運行查詢。也許查詢需要一些索引? 2)預處理這些信息,並將其放入另一個表或文件中。這樣,當谷歌要求它不會超時。

1

retCats.Add(pm.google_Category);需要這麼長時間的原因是因爲您正在參考一個延遲加載的對象,該對象執行另一次到服務器的往返。 如果你可以重構,所以你只需要一個本地副本的Id而不是整個對象它會加快這一部分。

如果您確實需要獲取整個對象,請調查在獲取productMappings時如何在單個查詢中將其拉下。如何做到這一點取決於你在SQL上使用的LINQ包裝器。

+0

一旦我得到第一瓶瓶頸固定,我會更多地考慮這一點。謝謝 – CountMurphy 2011-02-28 23:36:21

0

這也許應該工作:

var productMappings = GoogleProductMappingAccess.GetGoogleProductMappingsByID(context, productID); 
var categories = from pm in productMappings 
       where pm.GoogleCategoryId > 0 || 
         pm.GoogleCategoryMappingId > 0 
       orderby pm.MappingType descending 
       select pm.google_Category ?? 
         pm.google_CategoryMapping.google_Category; 

return categories.Take(numCategories); 

它的工作最好的,如果GetGoogleProductMappingsByID將返回IQueryable(如適用)。如果是這樣,LINQ會將整個語句轉換爲T-SQL命令,這將比內存LINQ更快。

隨意將.ToList()添加到最後一條語句中,使其與代碼中的返回類型相同(並強制執行LINQ語句)。

檢查.HasValue和> 0是無用的。檢查Id> 0就足夠了。
For more info:http://msdn.microsoft.com/en-us/library/2cf62fcy.aspx(operators)

+0

我現在就給這個去吧。同意。價值。表中的所有內容都是1或0 – CountMurphy 2011-03-01 00:16:05

+0

編譯錯誤:查詢正文必須以select子句或group子句結尾。奇怪的是我看到select子句,所以我不知道是什麼讓編譯器抱怨 – CountMurphy 2011-03-01 00:32:07

+0

啊我認爲這是我輸入'desc'而不是'降序'的錯誤。對不起,我沒有Visual Studio輸入這個,所以沒有語法驗證:) – Zyphrax 2011-03-01 14:03:25