对于.toarray()的慢速LINQ查询
本文关键字:LINQ 查询 toarray 对于 | 更新日期: 2023-09-27 18:13:05
我使用以下查询
foreach (var callDetailsForNode_ReArrange in callDetailsForNodes_ReArrange)
{
var test = from r1 in dtRowForNode.AsEnumerable()
join r2 in dtFileRowForNode.AsEnumerable()
on r1.Field<int>("Lng_Upload_Id") equals r2.Field<int>("Lng_Upload_Id")
where ((r1.Field<string>("Txt_Called_Number") == callDetailsForNode_ReArrange.caller2.ToString()) || r1.Field<string>("Txt_Calling_Number") == callDetailsForNode_ReArrange.caller2.ToString())
select r2.Field<string>("Txt_File_Name");
var d = test.Distinct();
}
到这里,这个查询在没有时间运行。但是当我添加
string[] str =d.ToArray();
strFileName = string.Join(",", str);
运行大约需要4-5秒。为什么添加.ToArray()
这么慢?
到这里为止,除了构建一个延迟执行模型来表示挂起的查询之外,实际上还没有完成任何事情。它不会开始迭代,直到你在迭代器上调用到目前为止,这个查询在任何时候都不会运行。
MoveNext()
,即通过foreach
,在你的例子中通过.ToArray()
。
所以:它需要时间,因为它在做工作。
考虑:
static IEnumerable<int> GetData()
{
Console.WriteLine("a");
yield return 0;
Console.WriteLine("b");
yield return 1;
Console.WriteLine("c");
yield return 2;
Console.WriteLine("d");
}
static void Main()
{
Console.WriteLine("start");
var data = GetData();
Console.WriteLine("got data");
foreach (var item in data)
Console.WriteLine(item);
Console.WriteLine("end");
}
这个输出:start
got data
a
0
b
1
c
2
d
end
请注意,工作并不是一次全部发生——它是延迟的(a
在got data
之后)和假脱机(我们没有得到a
,…, d
, 0
,…2
)。
相关:这是Distinct()
的大致工作方式,来自注释:
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source) {
var seen = new HashSet<T>();
foreach(var item in source) {
if(seen.Add(item)) yield return item;
}
}
…
和一个新的Join
操作:
public static string Join(this IEnumerable<string> source, string separator) {
using(var iter = source.GetEnumerator()) {
if(!iter.MoveNext()) return "";
var sb = new StringBuilder(iter.Current);
while(iter.MoveNext())
sb.Append(separator).Append(iter.Current);
return sb.ToString();
}
}
和使用:
string s = d.Join(",");
因为查询在迭代之前什么都不做,而.ToArray()
就是这样做的。
需要注意的一点是,一旦查询开始迭代,连接的右侧(在您的示例中,r2 in dtFileRowForNode.AsEnumerable()
)将被完全枚举,即使只访问结果的第一个元素-但在此之前不会。
如果你这样做了:
d.First()
r2 in dtFileRowForNode.AsEnumerable()
序列将被完全迭代(并在内存中缓冲),但只计算r1 in dtRowForNode.AsEnumerable()
的第一个元素。
(我应该指出这只适用于链接到对象。Linq-to-SQL将在数据库中运行这些查询,因此它处理缓冲。
您需要读取linq
语句的延迟求值。除非显式调用结果,否则查询不会完成,例如在foreach
中迭代,调用ToArray
, ToList
, Sum
, First
或其他计算查询的方法之一。
所以这是你的查询,需要这么多时间来完成,而不是ToArray
调用。