2009-02-23 10 views
17

ich diesen Code haben (ok, ich weiß nicht, aber etwas ähnliches: p)Sum() verursacht Ausnahme statt 0 zurückzugeben, wenn keine Zeilen

var dogs = Dogs.Select(ø => new Row 
    { 
      Name = ø.Name, 
      WeightOfNiceCats = ø.Owner 
       .Cats 
       .Where(æ => !æ.Annoying) 
       .Sum(æ => æ.Weight), 
    }); 

Ich gehe hier durch alle Hunde und resümieren die Gewicht (in eine nicht nullbare Dezimalzahl) aller nicht nervigen Katzen, die den gleichen Besitzer wie der Hund haben. Natürlich, so ziemlich alle Katzen ärgerlich, so bekomme ich diesen Fehler:

The null value cannot be assigned to a member with type System.Decimal which is a non-nullable value type.

Keines der Felder oder Fremdschlüssel verwendet null sein kann. Der Fehler tritt also auf, wenn die Klausel Where keine Katzen zurückgibt, was oft der Fall ist. Aber wie kann ich das lösen? Ich möchte, dass es 0 zurückgibt, wenn das passiert. Versuchte mit einem DefaultIfEmpty() nach der Where Klausel, aber dann bekomme ich diesen Fehler:

Object reference not set to an instance of an object.

Was ich denke, ist verständlich. Ich versuchte, ein ?? nach den Sum, hinzufügen, aber dann wird es nicht kompiliert aufgrund dieses Fehlers:

Operator '??' cannot be applied to operands of type 'decimal' and 'decimal'

Welche auch Sinn macht natürlich. Was kann ich also tun? Wäre schön wenn die Sum Sache gerade 0 zurückgibt wenn es nichts zu summieren gibt. Oder eine SumOrZero Aussage irgendeiner Art. Wäre es schwierig, eine SumOrZero Methode zu erstellen, die mit Linq2SQL funktioniert?

+1

Große Frage. Es ist eine Schande, dass dieses Verhalten nicht im MSDN dokumentiert ist. – Mykroft

Antwort

20

ist, was ich mit jetzt endete:

.Sum(æ => (decimal?) æ.Weight) ?? 0.0M, 

Dies funktioniert wie ein Charme.

Ich würde immer noch lieber eine SumOrZero hätte ich verwenden können, die genau wie die regulären Sum arbeiten würde, außer es würde nie null aus irgendeinem Grund zurückgeben. Wie die anderen bemerkt haben, wäre dies ziemlich einfach zu machen für IEnumerable, aber ein bisschen ekliger für IQuearyable zu erstellen, so würde es mit Linq2SQL usw. funktionieren. Also werde ich es jetzt für jetzt lassen. Obwohl, wenn jemand eines Tages gelangweilt ist und einen SumOrZero für IQueryables schreibt, der mit Linq2SQL für alle numerischen Typen arbeitet, lassen Sie mich bitte wissen: D

+0

Kleiner Tippfehler - da sollte "dezimal" sein * –

+1

@Daniel Das stimmt! Und es hat nur 5 Jahre gedauert, bis jemand bemerkt und benachrichtigt hat: p – Svish

3

Der Trick, den Sie bereits gegeben haben (.Sum(æ => (decimal?) æ.Weight) ?? 0.0M), ist das Beste, was ich mir für dieses Szenario vorstellen kann, wenn es als Abfrage in der Datenbank ausgeführt wird. Ein bisschen nervig, aber es gibt schlimmere Dinge ...

Im Gegensatz zu LINQ-to-Objects können Sie nicht einfach Ihre eigene Erweiterungsmethode hinzufügen, da sie vom zugrunde liegenden Provider zugeordnet werden muss.

+0

genau. das war, was ich befürchtet hatte, hehe. – Svish

5

In LINQ to Objects wäre es einfach. Mit LINQ to SQL wird es schwieriger. Sie könnten Ihre eigene Erweiterungsmethode schreiben, die Queryable.Sum mit einem Ausdruck aufgerufen hat, der aus dem normalen Aufbau mit einer Umwandlung in den nullbaren Typ besteht. Ich vermute, du musst das tun? 0m im aufrufenden Code obwohl. Mit anderen Worten, könnten Sie tun:

.SumOrNull(æ => æ.Weight) ?? 0M, 

Wo die Signatur für .SumOrNull wäre:

public static decimal? SumOrNull<TSource, decimal>(this IQueryable<TSource>, 
    Func<TSource,decimal> projection) 

Sie könnten im Grunde, dass in Abfragbare unterstützt für alle Werttypen schreiben. Sie könnte schreiben Sie es generisch, und rufen Sie die geeignete Methode in Queryable mit Reflexion, aber das wäre auch icky.

Ich denke, Sie sind am besten mit dem, was Sie haben, um ehrlich zu sein.

+0

Dann denke ich, ich werde es so lassen wie ich .. – Svish

0

Sie möchten DefaultIfEmpty verwenden, das ein IEnumberable mit einem einzelnen Element von 0 zurückgibt und das semantisch dasselbe ist, das Sie benötigen.

Da Sie eine nullwertfähige Dezimalzahl haben, müssen Sie wahrscheinlich die zweite Version der Methode verwenden, die 2 Parameter verwendet.

+1

Ganz im Gegenteil; Die Dezimalzahl ist nicht nullfähig. Der * Workaround * für den LINQ-zu-SQL-Fehler besteht darin, die Berechnung für Nullwerte zuzulassen. –

+0

Dann ist es noch einfacher :) – leppie

+1

DefaultIfEmpty verursacht die "Objektverweis nicht auf eine Instanz eines Objekts festgelegt." – Svish

Verwandte Themen