Dapper不是全自动ORM,需手动管理连接和SQL,字段名必须字面匹配;多表JOIN用splitOn按列名切分;QueryMultiple须严格按顺序Read;参数化仅支持匿名对象传参,连接必须手动打开。

直接说结论:Dapper 不是“配置好就能用”的全自动 ORM,它要求你明确控制连接、手动写 SQL,并严格匹配字段名和类型顺序——但正因如此,它快、稳、没黑箱。
Query 为什么返回空或 null 字段
最常见原因是列名与 T 类属性名不匹配(大小写不敏感,但拼写必须一致)。比如数据库列是 user_name,而类里写的是 UserName,Dapper 默认不会做下划线转驼峰;它只认字面匹配。
- 解决办法:SQL 中用别名显式对齐,例如
SELECT user_name AS UserName FROM users - 或者全局启用自动映射(需额外配置):注册
SqlMapper.AddTypeMap或使用第三方扩展如 Dapper.FluentMap - 注意:
QueryFirstOrDefault在无结果时返回default(T)(引用类型为null),不是异常,别误判为失败
多表 JOIN 后映射到多个类怎么写
Dapper 本身不解析 JOIN 结构,它靠 splitOn 参数切分结果集。这个参数不是表名,而是第二个类型第一个字段对应的列名(含别名)。
- SQL 必须按顺序 SELECT 所有字段,且第二张表字段要加别名避免冲突,例如:
SELECT u.id, u.name, d.id AS dept_id, d.name AS dept_name FROM users u JOIN depts d ON u.dept_id = d.id - 调用时指定
splitOn: "dept_id",Dapper 才知道从哪一列开始算第二个对象 - 映射委托必须按 SELECT 列顺序传参:
(u, d) => new UserWithDept { User = u, Dept = d } - 如果漏写别名或
splitOn值错位,会抛InvalidOperationException或字段全为默认值
QueryMultiple 为什么 Read() 报错
错误通常是因为没按 SQL 中语句顺序调用 Read,或者跳过了某个结果集。Dapper 的 GridReader 是游标式读取,不可回退、不可乱序。
- SQL 必须用分号分隔多个查询,例如:
"SELECT * FROM Orders; SELECT * FROM OrderItems WHERE OrderId IN @ids" - 必须先
multi.Read,再() multi.Read,顺序错或少调一次都会崩() - 每个
Read返回的是() IEnumerable,不是单个对象;想取首项得接.FirstOrDefault() - 记得用
using包裹GridReader,否则连接可能泄漏
参数化查询防注入的硬规则
Dapper 只在你用匿名对象或 IDictionary 传参时才真正参数化。拼接字符串、用 $"..." 插入变量,哪怕看起来像参数,也照常被 SQL 注入。
- 正确写法:
conn.Query("SELECT * FROM Users WHERE Name = @name", new { name = inputName }) - 错误写法:
conn.Query—— 危险!($"SELECT * FROM Users WHERE Name = '{inputName}'") - 动态列名/表名不能参数化,必须白名单校验或用
SqlMapper预编译逻辑处理 - 数组参数用
@ids支持,但仅限于IN子句,且数据库驱动需兼容(SQL Server OK,MySQL 需MySqlConnector)
最容易被忽略的点是连接生命周期管理:Dapper 所有扩展方法都要求 IDbConnection 已打开。传进去却没 Open(),或者连接被提前释放,就会抛 InvalidOperation —— 它不帮你开,也不替你管。











