使用StringBuilder循环构建SQL查询时的参数引号问题
解决SQL拼接中字符串值缺少引号及SQL注入问题
首先,你的核心问题是当MatchValue2And不为null时,直接拼接字符串值没有添加单引号导致SQL语法错误,同时还存在潜在的SQL注入风险(直接拼接用户输入的字符串是高危操作)。另外你提到无法使用序号重复的命名参数,那我们可以分两种方式解决:
方案1:安全格式化字符串拼接(适合无法使用参数化的场景)
先写一个辅助方法来处理SQL字符串的格式化,自动处理null和单引号转义:
// 辅助方法:安全生成SQL中的字符串值,处理null和单引号转义 private string SafeSqlString(string input) { if (input == null) return "NULL"; // 转义单引号,避免SQL注入攻击 return $"'{input.Replace("'", "''")}'"; }
然后修改你的循环代码,用这个方法来格式化所有需要拼接的字符串值:
var sb = new StringBuilder(); for (var i = 0; i < query.BlackWhiteListFieldMatchProxy.Count; i++) { var matchProxy = query.BlackWhiteListFieldMatchProxy[i]; // 提前格式化所有需要的参数值 var matchValue2AndFormatted = SafeSqlString(matchProxy.MatchValue2And); var matchValue2OrFormatted = SafeSqlString(matchProxy.MatchValue2Or); var matchValue1Formatted = SafeSqlString(matchProxy.MatchValue1); sb.Append($@"( MatchType = {(int)matchProxy.MatchType} AND EntityType = {(int)matchProxy.EntityType} AND MatchFieldType = {(int)matchProxy.MatchFieldType} AND ( ( MatchValue = {matchValue1Formatted} OR MatchValue = {matchValue2OrFormatted} ) AND ( {matchValue2AndFormatted} is NULL OR MatchValue2 is NULL OR MatchValue2 = {matchValue2AndFormatted} ) ) )"); if (i != query.BlackWhiteListFieldMatchProxy.Count - 1) sb.Append($@" OR"); }
这样不管MatchValue2And是null还是有值,都会生成正确的SQL语法:
- 当值为null时:
NULL is NULL - 当值不为null时:
'xxx' is NULL(带单引号,且单引号已转义)
方案2:参数化查询(推荐,彻底避免SQL注入)
如果你用的是Dapper、EF Core或者ADO.NET,推荐用参数化查询来彻底解决问题,同时避免参数名称冲突。这里以Dapper为例,我们给每个循环项的参数加上唯一前缀:
var sb = new StringBuilder(); var parameters = new DynamicParameters(); // 需要引用Dapper for (var i = 0; i < query.BlackWhiteListFieldMatchProxy.Count; i++) { var matchProxy = query.BlackWhiteListFieldMatchProxy[i]; var paramPrefix = $"item_{i}_"; // 每个循环项的参数唯一前缀 // 添加参数到参数集合 parameters.Add($"{paramPrefix}MatchType", (int)matchProxy.MatchType); parameters.Add($"{paramPrefix}EntityType", (int)matchProxy.MatchType); parameters.Add($"{paramPrefix}MatchFieldType", (int)matchProxy.MatchFieldType); parameters.Add($"{paramPrefix}MatchValue1", matchProxy.MatchValue1); // 处理null值,映射到数据库的DBNull parameters.Add($"{paramPrefix}MatchValue2Or", matchProxy.MatchValue2Or ?? (object)DBNull.Value); parameters.Add($"{paramPrefix}MatchValue2And", matchProxy.MatchValue2And ?? (object)DBNull.Value); sb.Append($@"( MatchType = @{paramPrefix}MatchType AND EntityType = @{paramPrefix}EntityType AND MatchFieldType = @{paramPrefix}MatchFieldType AND ( ( MatchValue = @{paramPrefix}MatchValue1 OR MatchValue = @{paramPrefix}MatchValue2Or ) AND ( @{paramPrefix}MatchValue2And is NULL OR MatchValue2 is NULL OR MatchValue2 = @{paramPrefix}MatchValue2And ) ) )"); if (i != query.BlackWhiteListFieldMatchProxy.Count - 1) sb.Append($@" OR"); } // 执行查询(示例用Dapper) using var connection = new SqlConnection("你的连接字符串"); var results = connection.Query<YourEntityModel>(sb.ToString(), parameters);
这种方式不仅解决了引号问题,还彻底杜绝了SQL注入,同时每个参数都有唯一名称(比如item_0_MatchValue2And、item_1_MatchValue2And),不会出现参数映射冲突的问题。
内容的提问来源于stack exchange,提问作者Rostyslav Fedyk
相关产品推荐
相关产品推荐

