直接用 Dapper 查询数据库后,结果默认是
IDataReader或动态对象(如
dynamic),它本身不提供自动映射到 DTO 的能力。Dapper 也不内置类似 AutoMapper 的对象映射逻辑 —— 所以“Dapper 自动映射 DTO”其实是靠你手动调用 AutoMapper,而不是 Dapper 主动触发它。
为什么不能让 Dapper “自动”调用 AutoMapper?
Dapper 的设计哲学是轻量、显式、高性能。它只负责把 SQL 结果按字段名(或列序)映射到强类型对象的 同名 public 属性 上,不介入业务层的转换逻辑(比如属性重命名、类型转换、嵌套对象组装、条件映射等)。AutoMapper 则专注在对象到对象的复杂转换上,两者职责不同,天然互补但需手动桥接。
推荐做法:查完再映射(最清晰可控)
这是最常用、最易调试、也最符合分层原则的方式:先用 Dapper 获取领域模型(如 Entity)或匿名对象,再交给 AutoMapper 转成 DTO。
查询时用 Dapper 映射到实体类(或匿名对象),保持 SQL 和数据访问层干净 在应用服务层或查询处理器中,调用mapper.Map<mydto>(entity)</mydto>如果 DTO 字段少、结构简单,也可以用 Dapper 直接查 DTO(前提是字段名完全匹配):
conn.Query<usersummarydto>("SELECT Id, Name FROM Users")</usersummarydto>
进阶技巧:封装一个带 AutoMapper 的 QueryHandler
避免重复写“查 + 映射”,可封装通用方法:
public async Task<TDto> QuerySingleAsync<TEntity, TDto>(
string sql,
object param = null) where TEntity : class
{
var entity = await _connection.QuerySingleAsync<TEntity>(sql, param);
return _mapper.Map<TDto>(entity);
}这样业务代码就变成:
var userDto = await handler.QuerySingleAsync<user userdto>("SELECT * FROM Users WHERE Id = @id", new { id });</user>
注意避坑点
别在 Dapper 的Query<t></t>中传 DTO 类型,除非字段 100% 匹配(比如 DTO 没有构造函数参数、没私有 setter、所有属性名和 SQL 列名一致) AutoMapper 配置要提前注册好(如
CreateMap<user userdto>()</user>),否则运行时报错 若 SQL 返回多表 JOIN 数据,DTO 含嵌套对象(如
UserDto.Profile),Dapper 原生不支持自动展开;此时要么用
Query<t1></t1>手动组合,要么改用 AutoMapper 的 ProjectTo(配合 EF Core)—— Dapper 不支持 ProjectTo
基本上就这些。Dapper 和 AutoMapper 不是“绑定关系”,而是“协作关系”:一个管快读,一个管巧转。用好它们的关键是明确分工,不越界,不强求自动化。
