Dapper 默认将 DBNull.Value 映射为对应类型的默认值(如 int→0、int?→null),易掩盖空值问题;应优先使用可空类型,配合 TypeHandler、SQL COALESCE 或自定义映射安全处理 NULL。
Dapper 默认会将数据库中的 DBNull.Value 映射为对应 .NET 类型的默认值(如 null、0、false),但这种隐式转换容易掩盖空值问题,尤其在可空类型(int?、DateTime?)或自定义逻辑中需要显式区分 NULL 和默认值时。关键不是“怎么转”,而是“怎么安全地识别和处理”。
Dapper 在填充实体时,对字段值做如下判断:
DBNull.Value,且目标属性是引用类型(如 string、MyClass),则赋值为 null
int、DateTime),且未声明为可空(int?),Dapper 会尝试调用该类型的默认构造(如 default(int) → 0),**不抛异常**int?、DateTime?),DBNull.Value 会被正确映射为 null
当需要在映射前/后精确控制空值逻辑(比如记录日志、触发业务规则、统一转成特定默认值),推荐以下方法:
SELECT COALESCE(Price, 0) AS Price FROM Product,让 Dapper 始终收到非 null 值reader.IsDBNull(index),再决定赋值逻辑DBNull.Value(需搭配 [SqlMapper.TypeHandler] 或自定义映射器)若项目中大量存在某类字段(如所有 decimal 字段都应转为 0m 而非 null),可注册自定义 TypeHandler:
SqlMapper.AddTypeHandler(new DecimalTypeHandler()); // 实现示例: public class DecimalTypeHandler : SqlMapper.TypeHandler{ public override void SetValue(IDbDataParameter parameter, decimal value) => parameter.Value = value; public override decimal Parse(object value) => value == DBNull.Value ? 0m : Convert.ToDecimal(value); }
这样所有 decimal 列都会按你的规则解析,无需改实体或 SQL。
几个常见误区要避开:
NULL —— Dapper 不会报错,但语义丢失(比如 int Age 收到 NULL 变成 0,无法区分“年龄未知”和“年龄为 0”)== null 判断数据库 NULL —— 对非可空类型永远为 false;应优先
用可空类型 + 显式 null 检查QuerySingleOrDefault() 时注意:若查询无结果返回 default(T),这和 DBNull 是两回事,别混淆基本上就这些。核心是:用可空类型承接可能为 NULL 的列,必要时用 TypeHandler 或 SQL 层统一兜底。不复杂但容易忽略细节。