2010-06-10 151 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