Dapper 防 SQL 注入,同一条 SQL 支持多种数据库

Dapper 防 SQL 注入,同一条 SQL 支持多种数据库
最新回答
素婉纤尘

2026-07-19 10:33:21

Dapper 可以通过参数化查询防止 SQL 注入,并通过实现“伪位置参数”(Pseudo-Positional Parameters)功能支持多种数据库的参数格式差异。

防 SQL 注入的原理

Dapper 默认使用参数化查询,将 SQL 语句中的参数值与 SQL 语句本身分离,避免直接拼接字符串导致的注入风险。例如:

using (IDbConnection conn = CreateConnection()) { string sqlCommandText = @"SELECT * FROM USERS WHERE ID=@ID"; Users user = conn.Query<Users>(sqlCommandText, new { ID = 2 }).FirstOrDefault(); Console.WriteLine(user.Name); }

此代码中,@ID 是参数占位符,实际值通过匿名对象 new { ID = 2 } 传递,Dapper 会将其转换为数据库兼容的参数格式,从而防止 SQL 注入。

不同数据库的参数格式差异

不同数据库对参数占位符的语法要求不同:

  • SQL Server:使用 @ 前缀(如 @ID)。
  • Oracle:使用 : 前缀(如 :ID)。
  • OleDB/ODBC:使用 ? 作为占位符,且不支持命名参数。

若直接使用 @ID 编写 SQL,在 Oracle 数据库中会报错,因为其仅支持 :ID 格式。

Dapper 的“伪位置参数”功能

Dapper 内部通过 Pseudo-Positional Parameters 机制,为不支持命名参数的数据库(如 OleDB)提供兼容性支持。其核心逻辑如下:

  1. 代码实现:Dapper 在执行 SQL 前,会扫描语句中的参数占位符(如 ?region?),将其替换为通用占位符 ?,并将参数按顺序绑定到命令对象。例如:// 原始 SQLselect * from Documents where Region = ?region? and OwnerId in ?users?// 替换后 SQLselect * from Documents where Region = ? and OwnerId in (?,?,?)
  2. 适用场景:主要解决 OleDB 等仅支持位置参数的数据库的兼容性问题。
自定义“伪位置参数”实现

为支持更多数据库(如 Oracle 和 OleDB),可自定义参数替换逻辑,通过正则表达式动态调整占位符格式:

private static readonly Regex pseudoRegex = new Regex(@"$([p{L}_][p{L}p{N}_]*)$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled); public static string ReplacePseudoParameter(IDbConnection conn, string cmdText) { return pseudoRegex.Replace(cmdText, match => { var key = match.Groups[1].Value; if (conn is OleDbConnection) { return "?"; // OleDB 仅支持 ? } return (conn is OracleConnection ? ":" : "@") + key; // Oracle 用 :,其他用 @ }); }

使用方式:在参数名两侧添加 $ 符号,调用 ReplacePseudoParameter 方法动态替换占位符:

string sqlCommandText = @"SELECT * FROM USERS WHERE ID=$ID$"; Users user = conn.Query<Users>(ReplacePseudoParameter(conn, sqlCommandText), new { ID = 2 }).FirstOrDefault();
  • SQL Server:替换为 @ID。
  • Oracle:替换为 :ID。
  • OleDB:替换为 ?(需结合 Dapper 的伪位置参数功能处理多参数场景)。
总结
  1. 防 SQL 注入:Dapper 的参数化查询通过分离 SQL 与参数值,从根本上避免注入风险。
  2. 多数据库支持:

    默认支持命名参数的数据库(如 SQL Server、MySQL)直接使用 @param 格式。

    通过自定义“伪位置参数”逻辑,可兼容 Oracle(:param)和 OleDB(?)等特殊格式。

  3. 扩展性:通过正则表达式和连接类型判断,可灵活适配更多数据库提供程序。

此方案在保持 Dapper 轻量级特性的同时,解决了跨数据库参数格式差异问题,适用于需要支持多种数据库的通用数据访问层开发。