2016-06-24 17 views
1

Ich habe einen Thread, der regelmäßig eine meiner MS SQL-Tabellen für alle Datensätze überprüft, deren Bitfeld "Processed" auf 0 gesetzt ist. Der Thread führt dann Code aus Verwenden dieser Datensätze und setzt dann ihre verarbeiteten Bits auf 1; Verwenden Sie es im Grunde als eine Warteschlange. Die Linq-Abfrage, die ich diese Datensätze abzurufen bin mit erstreckt sich über mehrere Zeilen und ist ziemlich kompliziert (aus Gründen unwichtig für die Frage), so ist hier eine sehr vereinfachte Version:So verwenden Sie Linqs .Count() -Methode in einer bedingten Anweisung

var RecordsToProcess = MyTable.Where(i => i.Processed == 0); // Very simplified 

ich, bis alle warten, um die Aufzeichnungen wurden, bevor Sie weiterverarbeitet, also würde ich so etwas wie dies nutzen möchten:

while (RecordsToProcess.Count() > 0) 
{ 
    System.Threading.Thread.Sleep(1000); 
} 

das Problem ist, dass während der Faden in der Tat Prozess die Aufzeichnungen und setzt ihre verarbeitete Bit auf 1, den Wert von RecordsToProcess. Count() in der Bedingungsanweisung nimmt niemals ab und somit erhalten wir eine Endlosschleife. Meine Schätzung ist, dass das Aufrufen von .Count() diese Ganzzahl im Speicher speichert und dann jede Iteration der Schleife diesen Wert untersucht, anstatt die Datenbank abzufragen, um die aktuelle Anzahl zu erhalten. Ich kann das Verhalten Ich möchte erhalten, indem die Abfrage in die Bedingungsanweisung bewegt wie so:

while (true) 
{ 
    if (MyTable.Where(i => i.Processed == 0).Count() > 0) 
     System.Threading.Thread.Sleep(1000); 
    else 
     break; 
} 

Da die Abfrage ich eigentlich in diesem Beispiel ist viel komplizierter als die Verwendung macht es auf diese Weise macht es schwer zu lesen. Kann ich etwas verwenden, das RecordsToProcess.Count()> 0 ähnlich ist, aber diese Datenbank jede Iteration abfragt, anstatt die Anfangszählung zu verwenden, die im Speicher gespeichert wurde (vorausgesetzt, dass ich richtig liege)?

Hinweis: Normalerweise würde ich keine potentiell gefährliche While-Schleife verwenden, aber ich muss diese Seite nur maximal 4 oder 5 Mal und dann nie wieder ausführen. Ich mache mir deswegen keine Sorgen.

+0

„Meine Vermutung ist, dass Aufruf .Count() speichert, die ganze Zahl im Speicher“ - IIRC das erste Mal, wenn Sie 'RecordsToProcess.Count() aufrufen ',' RecordsToProcess' wird ausgewertet und das Ergebnis dieser Auswertung wird zwischengespeichert. Also hast du recht. Meine Lösung wäre "var count = MyTable.Where(). Blah(). Blah(). Long(). Query()' mit geeigneten Zeilenumbrüchen usw. für die Lesbarkeit, und dann "if (count> 0) {... } '- oder verstecken Sie den LINQ in einer Methode,' GetProcessedRecords() '. –

+0

Alles hängt letztendlich von Ihrer 'RecordsToProcess' Abfrage ab. Sie sollten uns zeigen, was diese Abfrage ist. Sie haben es wahrscheinlich so geschrieben, dass es bereits ausgewertet ist. –

+0

Natürlich sehe ich den Rest deines Codes nicht, aber kannst du nicht einfach auf den Thread warten? Oder nicht mal in einem anderen Thread? –

Antwort

4

Editierten ursprünglichen Beitrag basierend auf den Kommentaren.

Ich glaube, ein Teil des Problems ist, wie der Compiler Schleifen optimiert.

Es ist wahrscheinlich etwas in Ihrer Abfrage, die Daten zwischenspeichert. Wenn die gesamte Abfrage eine faule Auswertung verwendet, außer dass die Count in der Schleife überprüft wird, wird jedes Mal, wenn Sie Count für die Abfrage aufrufen, eine erneute Auswertung durchgeführt. In Ihrem zweiten Beispiel befindet sich die gesamte Abfrage in der Schleife und muss daher jedes Mal neu bewertet werden, unabhängig davon, ob tatsächlich eine verzögerte Auswertung verwendet wird oder nicht. Ich würde die remarks in der MSDN-Dokumentation auf die Operatoren überprüfen, die Sie verwenden.

Ich würde auch vorschlagen, Any anstelle von Count in dieser Situation für Leistung und Klarheit verwenden. Abhängig davon, was Sie iterieren, Count wird in der Regel über eine Sammlung durchlaufen, um zu sehen, wie viele Elemente, aber Any ist fauler. In LINQ to Object ist Count() für Sequenzen optimiert, die ICollection implementieren, um die Count-Eigenschaft zu verwenden, die viel schneller ist als das Iterieren, und Any() stoppt die Überprüfung, nachdem sie 1 Element gefunden hat. Wie von Erik unten vorgeschlagen, gibt es in LINQ to SQL wahrscheinlich so etwas wie eine , die zu der SELECT-Anweisung hinzugefügt wurde. Ich würde annehmen, SQL hat seine eigene COUNT Optimierung, aber ich habe keine Nachforschungen gemacht.

Any() Verwendung bei Bedarf auch die Lesbarkeit helfen kann, indem sie in Count() > 0 des Betreibers loszuwerden, und mehr klar zum Ausdruck, dass Sie in einem bool interessiert sind kein int.

würde ich Ihre Methode wie folgt implementieren:

var query = MyTable.Where(i => i.Processed == 0); 
while(true) { 
    if (!query.Any()) break; 
    Thread.Sleep(1000); 
} 

oder besser noch das, wenn man es gemächlich bekommen auszuführen:

var query = MyTable.Where(i => i.Processed == 0); 
while(query.Any()) { Thread.Sleep(1000); } 

Wie jedoch von anderen Antworten erwähnt, weitere Informationen darüber, wie Ihre Abfrage aufgebaut ist, wäre hilfreich.

+1

* Count muss eine Sammlung wiederholen, um zu sehen, wie viele Datensätze * nicht immer wahr sind, [hängt von der Sammlung ab] (http://stackoverflow.com/a/7969468/209259). Mit den Begriffen Linq2Sql und EF bin ich mir ziemlich sicher, dass 'Any()' die Auswahl in eine 'Top 1' ändert und dann einfach prüft ob ein Datensatz vorhanden ist oder nicht (und womöglich auch nur '1' auswählt) von .. ", um den Netzwerkverkehr zu minimieren. –

+2

Ich bezweifle, dass der Compiler eine solche Optimierung vornehmen würde. Das wäre eine unsichere Änderung. Die Bedingung der while-Schleife wird _always_ evaluiert, sie wird nicht so zwischengespeichert, wie Sie meinen. –

0

Sie sind nicht die RecordsToProcess Variable auf jeder Schleife erfrischende

While(RecordsToProcess.Count() > 0) 
{ 
    System.Threading.Thread.Sleep(1000); 
    RecordsToProcess = MyTable.Where(i => i.Processed == 0); 
} 
+0

Dies ist nicht wirklich anders als das zweite Beispiel im ursprünglichen Beitrag. – JamesFaix

Verwandte Themen