Dapper 默认将 DBNull.Value 映射为对应类型的默认值(如 int→0、int?→null),易掩盖空值问题;应优先使用可空类型,配合 TypeHandler、SQL COALESCE 或自定义映射安全处理 NULL。

Dapper 默认会将数据库中的 DBNull.Value 映射为对应 .NET 类型的默认值(如 null、0、false),但这种隐式转换容易掩盖空值问题,尤其在可空类型(int?、DateTime?)或自定义逻辑中需要显式区分 NULL 和默认值时。关键不是“怎么转”,而是“怎么安全地识别和处理”。
理解 Dapper 对 DBNull 的默认行为
Dapper 在填充实体时,对字段值做如下判断:
- 如果数据库列值是
DBNull.Value,且目标属性是引用类型(如string、MyClass),则赋值为null - 如果是值类型(如
int、DateTime),且未声明为可空(int?),Dapper 会尝试调用该类型的默认构造(如default(int) → 0),**不抛异常** - 如果是可空值类型(
int?、DateTime?),DBNull.Value会被正确映射为null
手动检查 DBNull 的几种实用方式
当需要在映射前/后精确控制空值逻辑(比如记录日志、触发业务规则、统一转成特定默认值),推荐以下方法:
-
使用查询时的 SQL COALESCE 或 CASE:在数据库层处理,例如
SELECT COALESCE(Price, 0) AS Price FROM Product,让 Dapper 始终收到非 null 值 -
用 Query
配合 Func :在回调中手动判断自定义映射 reader.IsDBNull(index),再决定赋值逻辑 -
在实体属性的 setter 中拦截:配合私有 backing field,setter 内检查传入值是否为
DBNull.Value(需搭配[SqlMapper.TypeHandler]或自定义映射器)
推荐:用 TypeHandler 统一处理 DBNull(适合全局策略)
若项目中大量存在某类字段(如所有 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 层统一兜底。不复杂但容易忽略细节。










