簡體   English   中英

.ToArray()的慢LINQ查詢

[英]Slow LINQ query for .ToArray()

我正在使用以下查詢

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 ,..., d0 ,... 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()序列中的r2 in dtFileRowForNode.AsEnumerable()將完全迭代(並在內存中緩沖),但只會計算r1 in dtRowForNode.AsEnumerable()r1 in dtRowForNode.AsEnumerable()的第一個元素。

因此,如果連接中的一個序列比另一個序列大得多,則將大序列放在連接的左側會更有效(以內存方式)。 連接右側的整個序列將緩沖在內存中。

(我應該指出,只適用於Linq-to-objects.Linq-to-SQL將在數據庫中運行這些查詢,因此它處理緩沖。)

您需要閱讀對linq語句的默認評估。 除非您明確地調用結果,否則查詢未完成 - 例如在foreach迭代,調用ToArrayToListSumFirst或其他評估查詢的方法。

因此,您的查詢需要花費很長時間才能完成,而不是ToArray調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM