C#中如何正确参数化SQL查询以避免SQL注入?
现有代码问题分析
- 你当前的参数化写法确实无法正常运行,核心问题有3个:
- 参数名重复冲突:每次循环你都往SQL里追加同一个
:MG_GROUP_ID占位符,最终SQL里会有N个同名占位符,数据库无法区分每个占位符对应的实际值,会导致参数匹配完全错误。 - OleDb适配问题:OleDb驱动本身不支持命名参数,只会按占位符出现的先后顺序匹配参数,你写的
:参数名格式本质和?占位符没有区别,就算参数名不重复也会有顺序匹配的隐患。 - 逻辑冗余错误:循环内部每次都新建连接、命令对象,且只赋值了CommandText和参数,没有实际执行查询的逻辑,完全不符合原始代码拼接完整SQL后统一执行的逻辑。
- 参数名重复冲突:每次循环你都往SQL里追加同一个
正确参数化实现方案
因为你是动态生成查询列,每个DECODE函数里的MG_GROUP_ID都是独立的固定值,我们需要给每个值分配唯一的占位符,统一收集参数后再执行:
// 初始化SQL和全局参数列表 StringBuilder sqlBuilder = new StringBuilder(); List<OleDbParameter> parameters = new List<OleDbParameter>(); int paramIndex = 0; // 拼接Original列部分 foreach (DataRow row in dsMrgnGroups.Tables[0].Rows) { // 每个占位符用唯一序号区分,OleDb用?做占位符 sqlBuilder.AppendFormat("LTRIM(TO_CHAR(MAX(DECODE(CMD_MGG_ID, ?, CMD_MRGN_ORG_VALUE)), '999999999999999990.99')) AS Original_{0},", paramIndex); // 直接传原值,不要ToString()避免类型转换错误 parameters.Add(new OleDbParameter($"@MG_GROUP_ID_{paramIndex}", row["MG_GROUP_ID"])); paramIndex++; } // 拼接MemUploaded列部分 foreach (DataRow row in dsMrgnGroups.Tables[0].Rows) { sqlBuilder.AppendFormat("LTRIM(TO_CHAR(MAX(DECODE(CMD_MGG_ID, ?, CMD_MRGN_UPD_VALUE)), '999999999999999990.99')) AS MemUploaded_{0},", paramIndex); parameters.Add(new OleDbParameter($"@MG_GROUP_ID_{paramIndex}", row["MG_GROUP_ID"])); paramIndex++; } // 去掉末尾多余的逗号 if (sqlBuilder.Length > 0) { sqlBuilder.Length--; } // 补全SQL剩余部分(替换成你原始代码里的FROM、WHERE、GROUP BY等逻辑) sqlBuilder.Insert(0, "SELECT "); sqlBuilder.Append(" FROM YOUR_TABLE_NAME"); // 统一执行查询 using (OleDbConnection conn = new OleDbConnection(_connString)) using (OleDbCommand cmd = conn.CreateCommand()) { try { conn.Open(); cmd.CommandText = sqlBuilder.ToString(); cmd.Parameters.AddRange(parameters.ToArray()); // 这里补充你的查询执行逻辑,比如ExecuteReader、ExecuteScalar等 } catch (Exception ex) { _messageObj.WriteLog("Exception: DLL ->UpdateMarginsDLL -> GetMarginDetailsCM: " + ex.Message, _tmid, 1); } }
函数返回参数化查询的实现
你可以自定义一个简单的传输类,或者直接返回元组,把SQL和参数一起返回即可,示例:
// 自定义传输类 public class ParameterizedSql { public string SqlText { get; set; } public List<OleDbParameter> Parameters { get; set; } } // 函数返回示例 public ParameterizedSql BuildMarginQuery(DataSet dsMrgnGroups) { // 此处放上述拼接SQL、收集参数的逻辑 return new ParameterizedSql { SqlText = sqlBuilder.ToString(), Parameters = parameters }; }
调用方拿到返回值后,直接把SqlText赋值给CommandText,遍历Parameters添加到命令的参数集合即可执行。
注意事项
- 尽量用
StringBuilder拼接SQL,避免高频字符串拼接的性能损耗 - 传参数时不要手动转成字符串,保持原始数据类型,避免数据库隐式转换导致索引失效或者查询错误
- 所有实现IDisposable的对象(连接、命令、DataReader等)都要用
using包裹,自动释放资源
内容的提问来源于stack exchange,提问作者Alok Sharma
相关产品推荐
相关产品推荐

