Dapper为SingleOrDefault返回null(DATEDIFF(...))

时间:2012-10-29 18:53:54

标签: asp.net-mvc-3 sql-server-2008 dapper

我有一个类似于:

的SQL语句

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

如果Data没有ProjectId的任何记录,则SQL Server返回null。

在Dapper中,我通过以下方式执行此操作:

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

在这种情况下,我希望SingleOrDefault的语义意味着“如果这是null,则返回零。”事实上,我的代码更加零友好:

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

当我调试并进入此代码时,我发现行yield return (T)func(reader)正在抛出空指针异常。

我在这里做错了什么,或者这是设计的?

(仅供参考,解决方法是将我的选择包装在ISNULL(..., 0)

1 个答案:

答案 0 :(得分:2)

  

如果Data没有ProjectId的任何记录,SQL Server将返回null。

Data没有任何匹配记录的情况下,SQL服务器实际上不返回null - 它不返回任何行。这种情况很好:

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

两者都很好。你说ISNULL“修复”它意味着你正在谈论一个不同的场景 - “我返回行”场景。既然如此,你的代码所说的是“把这个包含null的1或更多整数,并将其映射为不可为空的int” - 是不可能的,并且映射器正确抛出异常。相反,你想要的是:

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

现在,如果有行,则将值映射到int?,然后然后应用单一或默认值。你希望它作为一个int,然后可能:

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

如果您需要能够区分“零行”和“空结果”,那么我建议:

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
}

或类似的东西