SQL WHERE IN与List<string>配合失效问题排查
问题根源分析
你遇到的核心问题是误解了FromSqlRaw的参数作用:它的参数是用于参数化查询的,不是用来直接拼接SQL片段的。
当你把string.Join(",", allowedStatus.Select(s => "'" + s + "'"))作为第二个参数传入时,EF Core会把这个完整的字符串(比如'de')当作一个单一的参数值,而不是把它解析成IN子句里的选项。实际执行的SQL会变成这样:
SELECT computerimport.hostname FROM ComputerImport WHERE LOWER(SUBSTRING(computerimport.hostname, 1, 2)) IN (@p1) AND computerimport.hostname LIKE '%' + @p0 + '%'
其中@p1的值是'de'(包含单引号),这意味着你在查询**开头两个字符等于'de'(带引号)**的主机名,自然找不到符合条件的结果。
而你直接传入"DE"时能生效,是因为此时@p1的值是DE,LOWER(SUBSTRING(...))得到的de和DE在数据库的不区分大小写比较中匹配(取决于你的数据库配置),但这只是巧合,并不是正确的用法。
正确解决方案
方案1:动态构建参数化的IN子句(推荐,避免SQL注入)
我们需要为allowedStatus中的每个元素生成独立的参数占位符,然后把这些元素作为单独的参数传入:
string searchString = ""; List<string> allowedStatus = new List<string> { "de" }; // 为每个状态生成一个参数占位符(比如@p0, @p1...) var statusPlaceholders = string.Join(", ", allowedStatus.Select((_, idx) => $"@status{idx}")); // 构建包含所有参数占位符的SQL模板 string sqlQuery = @" SELECT computerimport.hostname FROM ComputerImport WHERE LOWER(SUBSTRING(computerimport.hostname, 1, 2)) IN ({0}) AND computerimport.hostname LIKE '%' + @search + '%'"; // 替换IN子句的占位符,这里用string.Format是安全的,因为占位符是我们自己生成的 sqlQuery = string.Format(sqlQuery, statusPlaceholders); // 准备参数列表:先加searchString,再加所有allowedStatus元素 var parameters = new List<object> { searchString }; parameters.AddRange(allowedStatus); // 执行查询 var result = _context.OverviewQueries.FromSqlRaw(sqlQuery, parameters.ToArray());
方案2:使用FromSqlInterpolated(更简洁,自动参数化)
FromSqlInterpolated支持插值字符串,EF Core会自动把插值变量转换成参数,避免SQL注入:
string searchString = ""; List<string> allowedStatus = new List<string> { "de" }; // 安全生成参数化的IN子句占位符 var statusParams = allowedStatus.Select((s, idx) => (ParamName: $"@status{idx}", Value: s)).ToList(); var safeStatusInClause = string.Join(", ", statusParams.Select(p => p.ParamName)); // 构建安全的SQL模板 var safeSqlQuery = $@" SELECT computerimport.hostname FROM ComputerImport WHERE LOWER(SUBSTRING(computerimport.hostname, 1, 2)) IN ({safeStatusInClause}) AND computerimport.hostname LIKE '%{searchString}%'"; // 执行查询,EF会自动处理参数化 var result = _context.OverviewQueries.FromSqlInterpolated(safeSqlQuery, statusParams.Select(p => p.Value));
关键注意点
- 永远不要直接把用户输入拼接成SQL字符串(会导致SQL注入),参数化查询是必须的。
FromSqlRaw的参数是值参数,不是SQL片段的占位符,不能用来拼接SQL语法(比如IN子句的选项、表名等)。如果需要动态生成SQL语法部分,必须自己构建安全的模板字符串,再传入对应的参数。
内容的提问来源于stack exchange,提问作者Stephan
相关产品推荐
相关产品推荐

