2013-05-10 5 views
5

Я использую следующий запросSlow LINQ запрос для .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(); 
} 

UPTO здесь этого запуска запроса в кратчайшие сроки. Но по мере того как я добавил

string[] str =d.ToArray(); 
strFileName = string.Join(",", str); 

Выполняется почти 4-5 секунд. Что делает его настолько медленным при добавлении .ToArray()?

+1

Прочтите первый ответ на [этот вопрос] (http://stackoverflow.com/q/215548/601179) – gdoron

+1

Делает ли .Distinct() запрос? –

+1

Nope. Это просто добавляет тот факт, что вам нужен только один элемент для запроса. –

ответ

15

UPTO здесь этот запуск запроса вмиг.

До здесь, он на самом деле не сделано ничего, кроме построения модели отложенного выполнения, который представляет ожидающий запроса. Он не начинается итерацию, пока вы не назовете MoveNext() на итераторе, то есть через foreach, в вашем случае через .ToArray().

So: требуется время, потому что это работа.

Рассмотрим:

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) и намотка (мы не 't получить 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(","); 
+2

Я не знал, что даже первая строка в методе 'Console.WriteLine (« a »);' будет напечатана не раньше 'foreach'. Поэтому я бы подумал, что 'a' пришел до' получения данных'. Иногда Линк по-прежнему удивляет. –

12

Поскольку запрос НЕ НИЧЕГО, пока вы не перебираете его, что делает .ToArray().

Следует отметить, что правая часть соединения (в вашем примере, r2 in dtFileRowForNode.AsEnumerable()) будет полностью перечислина КАК СКОРО, когда запрос начнет повторяться, даже если только первый элемент результата доступ к ним - но не до тех пор.

Так что если вы сделали:

d.First()

r2 in dtFileRowForNode.AsEnumerable() последовательность будет полностью итерация (и в буферной памяти), но только первый элемент r1 in dtRowForNode.AsEnumerable() будет оцениваться.

По этой причине, если одна из ваших последовательностей в соединении намного больше, чем другая, более эффективно (по памяти) помещать большую последовательность слева от соединения. Вся последовательность справа от соединения будет буферизирована в памяти.

(я должен указать на то, что относится только к Linq-на-объекты. Linq к SQL будет запускать эти запросы в базе данных, поэтому он обрабатывает буферизацию.)

+0

^^ что он сказал. Когда вы определяете запрос, вы делаете именно это - ** определяя ** запрос. Вы не используете его для получения результатов. Когда вы вызываете '.ToArray()', выполняется запрос и выполняется вся необходимая обработка (что требует времени). Тогда у вас есть массив результатов. – theyetiman

+0

Есть ли способ, которым я могу получить отдельные значения в запятой, сепарированной строке эффективно –

+0

Делает .Distinct() не запускает запрос ?? –

2

Вы должны прочитать на deffered оценку в linq отчетности. Запрос не завершен, если вы явно не вызываете результаты - например, итерации в foreach, вызывая ToArray, ToList, Sum, First или один из других методов, которые оценивают запрос.

Это ваш запрос, на который уходит столько времени, а не ToArray.