Dapper renvoie null pour SingleOrDefault (DATEDIFF (...))

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

Question

J'ai une instruction SQL similaire à:

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

Dans le cas où Data ne contient aucun enregistrement pour ProjectId , SQL Server renvoie null.

Dans Dapper, je l'exécute via:

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

Dans ce cas, je pense que la sémantique de SingleOrDefault signifie "si c'est nul, retourne zéro". En fait, mon code est encore moins convivial:

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

Lorsque je débogue et que j'entre dans ce code, je trouve que le yield return (T)func(reader) ligne yield return (T)func(reader) lance une exception de pointeur nul.

Est-ce que je fais quelque chose de mal ici, ou est-ce que c'est par conception?

(FYI, la ISNULL(..., 0) consiste à envelopper mon choix dans un ISNULL(..., 0) )

Réponse acceptée

Dans le cas où Data ne contient aucun enregistrement pour ProjectId, SQL Server renvoie null.

Dans le cas où Data ne possède aucun enregistrement correspondant, SQL Server ne renvoie pas vraiment null - il ne renvoie aucune ligne. Ce scénario fonctionne 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

les deux fonctionnent bien. Le fait que vous disiez ISNULL "corrige" cela signifie que vous parlez d'un scénario différent - le scénario "J'ai renvoyé des lignes". Comme c’est le cas, ce que votre code dit, c’est «prenez ces nombres entiers de 1 ou plus qui contiennent un null, et mappez-le comme un int non nullable» - ce n’est pas possible , et le mappeur est correct à lancer. une exception. Au lieu de cela, ce que vous voulez, c'est:

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

Maintenant, s'il y a des lignes, il mappe la valeur sur int? , puis en appliquant le paramètre unique ou par défaut. Ce que vous voulez en tant qu'int, alors peut-être:

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

Si vous devez être en mesure de faire la différence entre "zéro ligne" et "résultat nul", alors je suggère:

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
}

ou quelque chose de similaire




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi