Dapper devuelve nulo para SingleOrDefault (DATEDIFF (...))

asp.net-mvc-3 dapper sql-server-2008

Pregunta

Tengo una declaración SQL similar a:

SELECT DATEDIFF(Day, startDate, endDate) FROM Data WHERE ProjectId=@id

En el caso donde Data no tiene ningún registro para ProjectId , SQL Server devuelve null.

En Dapper, ejecuto esto a través de:

value = conn.Query<int>("...").SingleOrDefault()

En este caso, espero que la semántica de SingleOrDefault signifique "si esto es nulo, devuelve cero". De hecho, mi código es aún más amigable con los cero:

int toReturn = 0;
using (var conn = ...) {
  toReturn = conn.Query<int>("SELECT DATEDIFF(...))");
}
return toReturn;

Cuando depuro y paso a este código, encuentro que el yield return (T)func(reader) línea yield return (T)func(reader) arroja una excepción de puntero nulo.

¿Estoy haciendo algo mal aquí, o es esto por diseño?

(Para su información, la ISNULL(..., 0) es envolver mi selección en un ISNULL(..., 0) )

Respuesta aceptada

En el caso donde Data no tiene ningún registro para ProjectId, SQL Server devuelve null.

En el caso donde Data no tiene ningún registro coincidente, el servidor SQL realmente no devuelve nulo, no devuelve filas. Este escenario funciona bien:

var result = connection.Query<int>( // case with rows
 "select DATEDIFF(day, GETUTCDATE(), @date)", new { date = DateTime.UtcNow.AddDays(20) })
 .SingleOrDefault();
result.IsEqualTo(20);

result = connection.Query<int>( // case without rows
    "select DATEDIFF(day, GETUTCDATE(), @date) where 1 = 0", new { date = DateTime.UtcNow.AddDays(20) })
    .SingleOrDefault();
result.IsEqualTo(0); // zero rows; default of int over zero rows is zero

ambos funcionan bien. El hecho de que diga ISNULL "corrige" significa que está hablando de un escenario diferente: el escenario "Volví a filas". Dado que ese es el caso, lo que su código está diciendo es "tomar este entero de 1 o más que contiene un valor nulo, y asignarlo como un int no anulable" - eso no es posible , y el asignador es correcto para lanzar una excepción. En cambio, lo que quieres es:

int? result = connection.Query<int?>(...).SingleOrDefault();

Ahora, si hay filas, ¿está mapeando el valor a int? , y luego aplicando el single o default. Lo quieres como int, entonces tal vez:

int result = connection.Query<int?>(...).SingleOrDefault() ?? 0;

Si necesita saber la diferencia entre "cero filas" y "resultado nulo", le sugiero que:

class NameMe {
    public int? Value {get;set;}
}
var row = connection.Query<NameMe>("select ... as [Value] ...", ...)
                    .SingleOrDefault();
if(row == null) {
   // no rows
} else if(row.Value == null) {
   // one row, null value
} else {
   // one row, non-null value
}

o algo similar



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué