2009-02-23 5 views
17

que tienen este código (ok, no lo sé, pero algo similar: p)Suma() hace una excepción en lugar de devolver 0 cuando no hay filas

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

Aquí voy a través de todos los perros y resumir la peso (en un decimal no anulable) de todos los gatos no molestos que tiene el mismo dueño que el perro. Por supuesto, casi todos los gatos son molestos, por lo que me sale este error:

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

Ninguno de los campos o las claves externas utilizadas pueden ser nulo. Entonces, el error ocurre cuando la cláusula Where no devuelve gatos, lo que a menudo ocurre. Pero, ¿cómo puedo resolver esto? Quiero que devuelva 0 cuando eso suceda. Intentado con un DefaultIfEmpty() después de la cláusula Where, pero luego me sale este error:

Object reference not set to an instance of an object.

que supongo que es comprensible. He intentado añadir un ?? después de la Sum, pero entonces no lo puedo compilar debido a este error:

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

cual también tiene sentido, por supuesto. ¿Entonces Que puedo hacer? Sería bueno si la cosa Sum acaba de regresar 0 cuando no había nada que resumir. O una declaración SumOrZero de algún tipo. ¿Sería difícil hacer un método SumOrZero que funcionara con Linq2SQL?

+1

Gran pregunta. Es una pena que este comportamiento no esté documentado en MSDN. – Mykroft

Respuesta

20

Esto es lo que terminó por ahora:

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

Esto funciona como un encanto.

Todavía preferiría tener un SumOrZero que podría usar, que funcionaría exactamente como el Sum normal, excepto que nunca devolvería null por ningún motivo. Como los otros han notado, esto sería bastante fácil de hacer para IEnumerable pero un poco más difícil de crear para IQuearyable así que funcionaría con Linq2SQL, etc. Así que simplemente lo dejo por ahora. Aunque si alguien se aburre un día y escribe un SumOrZero para IQueryables que funciona con Linq2SQL para todos los tipos numéricos, hágamelo saber: D

+0

Pequeño error ortográfico - debería haber 'decimal' * –

+1

@Daniel ¡Eso es correcto! Y solo tomó 5 años para que alguien notara y notificara: p – Svish

3

El truco que ya diste (.Sum(æ => (decimal?) æ.Weight) ?? 0.0M) es lo mejor que se me ocurre para este escenario cuando se ejecuta como una consulta en la base de datos. Un poco molesto, pero hay cosas peores ...

A diferencia de LINQ-to-Objects, no puede simplemente agregar su propio método de extensión, ya que debe ser mapeado por el proveedor subyacente.

+0

exactamente. eso era lo que temía, jeje. – Svish

5

En LINQ to Objects sería fácil. Con LINQ to SQL será más difícil. Puede escribir su propio método de extensión que llamó al Queryable.Sum con una expresión creada a partir de la normal, con un molde al tipo que admite nulo. Sospecho que tendrías que hacer el ?? 0m en el código de llamada sin embargo. En otras palabras, se podría hacer:

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

Cuando la firma de .SumOrNull sería:

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

Se podría escribir que, básicamente, para todos los tipos de valores admitidos en consultables. Usted podría escribirlo genéricamente, y llamar al método apropiado en Queryable usando la reflexión, pero eso sería asqueroso también.

Creo que es mejor que tengas lo que tienes, para ser sincero.

+0

Entonces creo que lo dejo tal como lo hice ... – Svish

0

Desea usar DefaultIfEmpty, que devolverá un IEnumberable con un único elemento de 0, y que será semánticamente el mismo que requiera.

Como tiene un decimal con nulo, probablemente tenga que usar la segunda versión del método tomando 2 parámetros.

+1

Todo lo contrario; el decimal no es nulo. La * solución * al problema LINQ-to-SQL es hacer que sea anulable para el cálculo. –

+0

Entonces es aún más fácil :) – leppie

+1

DefaultIfEmpty provocó la "Referencia del objeto no establecida en una instancia de un objeto". – Svish

Cuestiones relacionadas