2010-06-10 154 views
2

讓我的腳溼潤Linq。我試圖確定四個DataColumns中包含的不同值。所以,我從訪問空Linq結果非常緩慢

var c1types = (from DataRow row in dtSource.Select("hasreq") 
       where row["m"].ToInt() > 0 
       select new { col = row["m"] }).Distinct(); 
var c2types = (from DataRow row in dtSource.Select("hasreq") 
       where row["w"].ToInt() > 0 
       select new { col = row["w"] }).Distinct(); 
var c3types = (from DataRow row in dtSource.Select("hasreq") 
       where row["ag"].ToInt() > 0 
       select new { col = row["ag"] }).Distinct(); 
var c4types = (from DataRow row in dtSource.Select("hasreq") 
       where row["aq"].ToInt() > 0 
       select new { col = row["aq"] }).Distinct(); 

foreach (var type in c1types.Union(c2types).Union(c3types).Union(c4types).Distinct()) 
{ 
    ... 
} 

這個工程,但是很慢(4-5秒)。於是,我把在foreach之前以下

MessageBox.Show(c1types.Count().ToString()); // 1 - immediate display 
MessageBox.Show(c2types.Count().ToString()); // 1 - immediate display 
MessageBox.Show(c3types.Count().ToString()); // 1 - immediate display 
MessageBox.Show(c4types.Count().ToString()); // 0 - 4-5 seconds to display 

用我的樣本數據中,每個前三個選擇的返回單個獨特的值(計數()== 1)。第四個返回沒有值(Count()== 0)。我不明白的是爲什麼它瞬間顯示前三個計數,但第四個顯示4-5秒。看起來空洞的結果是經濟放緩的原因。這裏發生了什麼,什麼是最好的解決方法?

+0

你使用.net 3.5或.net 4.0嗎? – Luke101 2010-06-11 00:22:40

+0

我正在使用3.5。 – PahJoker 2010-06-11 00:53:35

+0

我沒有太多的DataTable經驗 - 是每個查詢數據庫後端的查詢,還是隻查詢內存中的數據?如果一個數據庫,檢查sql(sql分析器,linqpad,無論什麼)可能有助於弄清楚發生了什麼(缺少索引?壞查詢?等) – 2010-06-11 06:34:46

回答

1

從你的代碼中,我猜ToInt()是一個擴展方法。我很確定DataRow上的索引器返回對象,我不記得定義ToInt()的Object。

如果這是真的,ToInt可能會做一些性能下降。也許這是做這樣的事情

try { return Int32.Parse(arg); } 
catch { return 0; } 

如果Int32.Parse無法處理大部分值來自行[「水性」],它可能會造成緩慢 - 檢查你的調試窗口例外。如果這是問題,你可以使用Int32.TryParse來加速它,它不會拋出異常。

如果我錯了,你能提供更多的信息嗎?什麼是ToInt()?

編輯:

我應該知道,這將是Convert.ToInt32,作爲Int32.Parse需要一個參數的字符串。我建議的解決方案如下。

批評使用Convert.ToInt32的:

你應該不Convert.ToInt32捕捉異常。它會拋出什麼Eric Lippert會稱之爲「Boneheaded」異常。例外,應該從不發生。如果你從Convert.ToInt32得到一個異常,你的程序是錯誤的。您已嘗試將某些內容轉換爲不代表Int32的Int32。考慮單元測試對於ToInt擴展方法的效果。你可以調用myPrizeSheep.ToInt()並獲得0.將一隻綿羊轉換爲數字是否有意義?吞下Convert.Int32的異常通常會導致麻煩 - 在您的情況下,這是一個性能問題,但通常情況會更糟 - 這是一個正確性問題。

沒有Convert.TryConvertInt32(Object)。雖然有一個Int32.TryParse(String)。這是因爲想要將用戶輸入的字符串解析爲Int32是很常見的。你期望他們可能輸入了一些不是Int32的東西 - 如果他們這樣做,這不是一個例外情況 - 你可以告訴用戶糾正它 - 這是正常程序執行流程的一部分。

如果你有一個對象,你需要必須知道它代表一個Int32,以便嘗試並轉換它。如果你傳遞給Convert.ToInt32並不代表Int32,那麼這是一個特例。我想不出一個你想「嘗試」將任何舊對象轉換爲Int32的實例 - 顯然,BCL開發者也不能。

我不認爲ToInt是很好的使用擴展方法。我通常使用擴展方法,所以我可以使用很好的管道轉發風格語法將呼叫鏈接在一起。有很少的情況下你想鏈ToInt()到其他方法調用。

因爲所有ToInt都會調用Convert.ToInt32並錯誤地吞下任何異常。在你的例子中使用Convert.ToInt32會更好。

解決辦法:

考慮如何處理事情,是不是在你的Linq查詢整數。在你的情況下,它們可能是空值或DBNull。很可能,你要排除那些行,在這種情況下,你可以沿着線寫的東西:

var c4types = (from DataRow row in dtSource.Select("hasreq") 
       where row["aq"] != null && row["aq"] != DBNull.Value && row["aq"].ToInt() > 0 
       select new { col = row["aq"] }).Distinct(); 

這表達了你最終使用對象而不是Int32s列表的缺點。你可以在最終選擇中做一個轉換,但是你會做兩次轉換。說實話,我的首選方式(如果你想Int32s的集合,而不是對象)將是:

var c4types = dtSource.Select("hasreq") 
         .Where(row => row["aq"] != null && row["aq"] != DBNull.Value) 
         .Select(Convert.ToInt32(row["aq"]) 
         .Where(i => i > 0) 
         .Distinct(); 

在上面,我們首先得到空值去掉行。然後我們轉換我們知道的行。然後我們去掉小於1的整數,最後得到一個獨特的整數集合。

+0

你是完全正確的,它是一個擴展方法,並且類似於你的代碼,但使用Convert.ToInt32()。當我有機會時,我會嘗試您的建議,並會在當時發佈我的調查結果。謝謝! – PahJoker 2010-07-09 22:04:04

+0

@pahjoker - 我應該知道它是Convert.ToInt32!看到我上面的編輯 - 不要採取我個人說的話,我只是想幫助您避免將來可能發生的問題,如果您繼續使用ToInt擴展方法:) – 2010-07-17 11:08:52