参数化查询必须使用@param占位符,禁止字符串拼接用户输入;动态条件应通过dynamicparameters按需添加参数;表名列名等标识符需用白名单或quotename()防护;连接须显式open();存储过程需指定commandtype。

参数化查询必须用 @param 占位符,不能拼字符串
直接拼接用户输入到 SQL 字符串里(比如 "SELECT * FROM Users WHERE Name = '" + name + "'")等于给攻击者递刀。Dapper 不会帮你“修复”这种写法——它只负责把传进去的参数安全绑定,但前提是 SQL 本身得是干净模板。
正确做法是:SQL 中只出现 @name、@age 这类命名占位符,值通过匿名对象或 DynamicParameters 传入。数据库引擎收到的是预编译结构 + 独立数据流,用户输入永远只当值处理,不参与语法解析。
-
@前缀不可省略,SQL Server 不认?或:name - 占位符名和参数对象属性名必须完全一致(大小写敏感)
- 传
null时 Dapper 自动转为DBNull.Value,不用手动判断 - 别用
$"SELECT ... WHERE Name = '{name}'"—— 这种写法哪怕变量内容干净,也破坏了参数化前提
动态条件怎么写才安全?用 DynamicParameters 而不是字符串拼接
当 WHERE 条件不确定(比如搜索页支持按姓名、年龄、状态组合筛选),不能退回到字符串拼接。动态 SQL 一样能参数化,关键是占位符和参数添加逻辑对齐。
错误示范:sql += " AND Name LIKE '%" + keyword + "%'" —— 无论 keyword 是否做过 Trim 或 IsNullOrWhiteSpace 判断,都已失守。
正确路径是构建 DynamicParameters,按需追加参数,并同步拼接对应占位符:
var sql = "SELECT * FROM Users WHERE 1=1";
var parameters = new DynamicParameters();
if (!string.IsNullOrEmpty(keyword))
{
sql += " AND Name LIKE @keyword";
parameters.Add("keyword", $"%{keyword}%");
}
if (minAge > 0)
{
sql += " AND Age >= @minAge";
parameters.Add("minAge", minAge);
}
var users = connection.Query<user>(sql, parameters);</user>
- 每个
parameters.Add()必须对应 SQL 中一个@xxx,漏掉或名字不匹配会报InvalidOperationException: No value given for parameter - 不要在 SQL 拼接中混用
@和变量名(如"AND Name = " + name),哪怕只有一处,整条语句就失效 - LIKE 的通配符
%必须写在 C# 侧(如"%" + keyword + "%"),不能塞进 SQL 字符串里
表名、列名、ORDER BY 字段不能参数化,得靠白名单或 QUOTENAME()
@sortColumn 在 ORDER BY @sortColumn 中是无效的——SQL Server 明确禁止参数化标识符。这类场景必须换思路。
最稳妥方式是预定义白名单:
var allowedSortColumns = new[] { "Name", "Age", "CreatedAt" };
if (!allowedSortColumns.Contains(sortBy)) throw new ArgumentException("Invalid sort column");
var sql = $"SELECT * FROM Users ORDER BY {sortBy} {sortOrder}"; // 注意:这里 sortBy 已确认合法
- 白名单必须硬编码或从配置加载,不能来自用户请求体或 query string 直接映射
- 若必须支持任意列名(如后台管理导出功能),可用
QUOTENAME()包裹:"ORDER BY " + connection.QuerySingle<string>($"SELECT QUOTENAME('{userInput}')")</string>,但要确保userInput长度合理且不含控制字符 - 千万别把白名单校验和参数化混为一谈——前者防结构注入,后者防值注入,两者缺一不可
连接没打开就查?Query<t>()</t> 会直接抛 InvalidOperationException
Dapper 只扩展 IDbConnection,不管理连接生命周期。常见错误是写了 using var conn = new SqlConnection(...) 却忘了在 using 块内调用 conn.Open(),或者用 EF Core 的 DbContext.Database.GetDbConnection() 后没检查状态。
- 显式调用
conn.Open()是必须步骤,不能依赖 Dapper 自动触发 -
using块只保证连接释放,不保证自动打开;如果连接未打开就执行Query<t>()</t>,会立刻崩在第一行 - 存储过程调用时,记得设
commandType: CommandType.StoredProcedure,否则 Dapper 当普通 SQL 执行,可能忽略输出参数 - 批量插入用
Execute("INSERT ...", list)时,Dapper 会自动展开为多行参数绑定,但前提是list里每个对象属性名和 SQL 中@xxx严格对应
真正容易被忽略的点在于:参数化只保“值”的安全,不保“结构”的安全。表名拼错会报错,但拼进恶意表名(如 Users; DROP TABLE Orders--)可能直接执行。白名单和 QUOTENAME() 不是可选项,是上线前必须落地的检查项。











