2009-08-17 11 views
12

Soy nuevo en LINQ y trato de crear algunos puntos de datos de una tabla a otra. Los tres campos de importancia en esta tabla son el ID, el tiempo y el valor. Estoy escribiendo una consulta para obtener el valor promedio durante un tiempo establecido para una identificación elegida. El LINQ He escrito sigue:Manejo de resultados nulos con el método LINQ Average()

var value = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select t.Value).Average(); 

Sin embargo, esto se bloquea en tiempo de ejecución con:

"El valor nulo no puede ser asignado a un miembro con el tipo System.Decimal que es un no anulable tipo de valor ... "

A ciertos intervalos no hay datos, por lo que el SQL LINQ genera devoluciones nulas, lo que me gustaría es COALESCED a 0, pero en su lugar falla la aplicación. ¿Hay alguna manera de escribir esta consulta LINQ para poder manejar esto correctamente?

La definición de la tabla para hacer las cosas más claras:

[Serializable] 
[Table(Name = "ExampleTable")] 
public class ExampleTable 
{ 
    [Column(Name = "Id")] 
    public int Id { get; set; } 

    [Column(Name = "Time")] 
    public DateTime Time { get; set; } 

    [Column(Name = "Value")] 
    public int Value{ get; set; } 
} 

Respuesta

19

Creo que quieres

var value = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select (int?)t.Value).Average() 

De esta manera, se obtiene una double? espalda, mientras que sin el (int?) elenco que necesita para obtener un double espalda, que no puede ser null.

Esto se debe a la firma

double Enumerable.Average(IEnumerable<int> source) 
double? Enumerable.Average(IEnumerable<int?> source) 

Ahora, para obtener un promedio de 0 en lugar de nulo, es necesario colocar al operador coalescente al final

var value = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select (int?)t.Value).Average() ?? 0.0; 

mi humilde opinión este es un diseño bastante horrible de la clase Enumerable/Queryable; ¿Por qué no puede Average(IEnumerable<int>) devolver double?, por qué solo para Average(IEnumerable<int?>)?

+0

Gracias que lo ha hecho. – Magpie

+0

solo para ayudar a otros en este tema: si la consulta * no devuelve ninguna fila *, nada realmente ayudará. solo tienes que evitar ejecutar la consulta por completo. Al menos esa fue mi experiencia. – horace

+0

@horace: Eso simplemente no es verdad. Promedio, Mín., Máx., Suma todo funciona cuando la consulta no coincide con filas * siempre que el resultado agregado sea nulo *. Como puede ver en la consulta anterior, necesitará un molde para aplicar la capacidad de anulación cuando la expresión para agregar no sea de tipo anulable. (En el raro caso de que todavía no funcione, probablemente estés lidiando con un proveedor de LINQ escamoso. Eso no es culpa de LINQ, y ningún proveedor que yo conozca se equivocó). – Ruben

15

EDIT: Cambio completo :)

bien, ¿qué tal esto:

var value = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select t.Value).DefaultIfEmpty().Average() 

Creo que es lógicamente lo que quiero - cambiando {} a {0}, haciendo todos los promedios alcanzables. No sé si hará lo que quieras en términos de SQL.

+0

Lo sentimos, esto no funciona, ya que el valor es una int no anulable. – Magpie

+0

Luego quite la "m" del 0 y debería funcionar bien. –

+0

Ah, me estoy acercando a esto de forma incorrecta ... espera. –

1

EDIT: Total de retrabajo

intenta transmitir el valor a nullable primera

var value = (from t in _table 
     where t.Id == id 
      && t.Time >= intervalStartTime 
      && t.Time <= intervalEndTime 
     select ((int?)t.Value) ?? 0).Average() 
+0

seleccione t.Value ?? 0 no compila como t.Valor es un int no nulo. – Magpie

+0

¿Has probado esta edición? ¿Cual es el resultado? –

+0

Sí, el compilador me dice que el operando de la izquierda nunca será nulo ya que el valor no se puede anular – Magpie

0

intente lo siguiente. Simplemente omitirá todos los elementos nulos devueltos por la consulta.

var value = (from t in _table 
      where t != null 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select t.Value).Average(); 

Si usted quiere tratar de manera explícita los elementos nulos como cero, entonces un simple uso de la operador condicional debe hacer el trabajo:

var value = (from t in _table 
      where t == null || 
       (t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime) 
      select t == null ? 0 : t.Value).Average(); 
+0

¿Cómo puede 't' ser nulo, y estar entre' intervalStartTime' y 'intervalEndTime'? Creo que su problema es que no hay registros entre ciertos tiempos de inicio y finalización, no que haya nulos en la tabla. – Blorgbeard

+0

Oh, t.Valor vs. t.Time - Ya veo. – Blorgbeard

+0

Gracias por la sugerencia, pero aún arroja la misma excepción. – Magpie

0

Podría utilizar una temperatura para la consulta inicial ?

por ejemplo:

var temp = (from t in _table 
      where t.Id == id 
       && t.Time >= intervalStartTime 
       && t.Time <= intervalEndTime 
      select t.Value) ?? new List<int>() {0}; 
var value = temp.Average(); 

No estoy seguro si esto ayuda.

Cuestiones relacionadas