如何使用FromSqlInterpolated安全实现SQL IN子句?
安全构建带IN子句的EF Core SQL查询(规避SQL注入)
当你用FromSqlRaw直接拼接IN子句的参数时,Sonar检测到SQL注入风险是因为直接字符串拼接了用户可控输入。而FromSqlInterpolated默认无法直接处理集合参数——EF Core会把整个集合当成单个参数传递,导致SQL语法错误或逻辑失效。下面是几种可行的安全实现方式:
方法1:用FromSqlInterpolated生成独立参数(推荐)
为集合中的每个元素创建独立的SQL参数,确保IN子句完全参数化:
var targetIds = new List<int> { 12, 34, 56, 78 }; // 为每个ID生成唯一参数 var parameters = targetIds.Select((id, idx) => new SqlParameter($"@id{idx}", id)).ToList(); // 拼接参数名作为IN子句的占位符 var paramNames = string.Join(", ", parameters.Select(p => p.ParameterName)); // 执行参数化查询 var result = dbContext.YourEntity .FromSqlInterpolated($"SELECT * FROM YourEntity WHERE Id IN ({paramNames})") .ToList();
这种方式会生成类似IN (@id0, @id1, @id2, @id3)的SQL,每个参数都是独立的,完全避免了注入风险,Sonar也不会触发告警。
方法2:用FromSqlRaw配合参数数组
如果你更习惯用FromSqlRaw,只要不直接拼接参数值,而是用占位符+参数数组,同样是安全的:
var targetIds = new List<int> { 12, 34, 56 }; // 生成对应数量的占位符 var placeholders = string.Join(", ", Enumerable.Range(0, targetIds.Count).Select(i => $"@p{i}")); var result = dbContext.YourEntity .FromSqlRaw($"SELECT * FROM YourEntity WHERE Id IN ({placeholders})", targetIds.ToArray()) .ToList();
EF Core会自动将数组中的每个元素绑定到对应的占位符上,实现参数化查询。
方法3:优先使用LINQ Contains(最简洁)
如果不需要手写SQL,直接用LINQ的Contains方法是最优解——EF Core会自动将其转换为参数化的IN子句:
var targetIds = new List<int> { 12, 34, 56 }; var result = dbContext.YourEntity.Where(e => targetIds.Contains(e.Id)).ToList();
这种方式无需手动处理参数,代码更简洁,同时完全规避注入风险。
常见误区
不要直接将集合作为单个参数传入IN子句,比如FromSqlInterpolated($"SELECT * FROM YourEntity WHERE Id IN ({targetIds})")——这会导致EF Core把整个集合对象转换成字符串(比如System.Collections.Generic.List1[System.Int32]`),既不符合SQL语法,也达不到预期查询效果。
内容的提问来源于stack exchange,提问作者Lucas Dalmarco
相关产品推荐
相关产品推荐

