如何比较 EF Core 插值查询中的整数列表

Posted

技术标签:

【中文标题】如何比较 EF Core 插值查询中的整数列表【英文标题】:How to compare a list of integers in EF Core Interpolated Query 【发布时间】:2022-01-19 22:57:13 【问题描述】:

我正在尝试将使用 EF Core 的 .FromSqlRaw() 方法运行的 sql 查询转换为使用 .FromSqlInterpolated() 运行的查询,这样它们就不太容易受到 SQL 注入攻击。我几乎把所有东西都转换了并且工作正常,但让我难过的一件事是如何通过单个字段的或配置中的整数列表进行过滤。 数据库是 postgres,引号是因为我首先使用代码 EF Core,这意味着所有表都像类一样大写。 ProjectTypeId 是我表中的整数列,projectTypes 是List<int> 类型变量。

我要替换的 where 子句中的代码是:

WHERE ""PartGroups"".""ProjectTypeId"" IN(string.Join(",", projectTypes))

我能够让它工作的最接近的方法是:

""PartGroups"".""ProjectTypeId""::text IN(string.Join(",", projectType))

""PartGroups"".""ProjectTypeId""::text LIKE ANY(ARRAY[string.Join(",", projectTypes)])

当 projectTypes 中只有一个值时,这些将起作用,但不止于此,它会失败。我不知道如何查看生成的查询+参数集,只是查询,所以我不确定它对参数做了什么,所以我一直在努力找出可行的方法。此外,该查询总共有大约 80 个参数,因此使用原始查询手动设置每个参数是不可行的。

【问题讨论】:

【参考方案1】:

您可以接近它的一种方法,也许是利用 EF 的能力在原始的顶部进行组合

context.SomeTable
  .FromSqlInterpolated($"SeLeCt t.* FrOm ...")
  .Where(st => idList.Contains(st.id))

EF 会将您的 SQL 作为子查询放入,并在外部为您编写 IN。如果可以的话,数据库查询优化器将(可能)然后将 IN 推送到子查询中..

SELECT ...
FROM 
  (
    SeLeCt t.* FrOm ...
  ) x
WHERE x.ID IN (.., ..)

如果您想查看创建的 SQL EF 并且您使用的是 EF5+,那么最简单的方法是执行 ToList/ToArray 之外的所有操作,并捕获可查询的内容:

var q = context.Thing.Where(...)

然后在调试器中暂停并查看q 的DebugView 属性。您将获得完整的 SQL 文本,您可以将其插入 SSMS 并检查执行计划,检查它是否像单级(非分层)查询一样运行..

【讨论】:

【参考方案2】:

您可以做的另一件事是自己在 FormattableString 中创建查询。只要 FromSqlInterpolated 接收到一个 FormattableString ,它就会把它拆开并参数化

    var args = new object[]  your, single, arguments, here.Concat(yourListOfThingsHere.Cast<object>()).ToArray();

    var fs = FormattableStringFactory.Create(@"SELECT
  some,
  columns/0,
  here 
FROM
  table t
WHERE  
  someColumn = 1 AND
  otherColumn BETWEEN 2 and 3 AND 
  columnToBeINned IN(" + string.Join(",", Enumerable.Range(4, yourListOfThingsHere.Count)) + @")
GROUP BY some, columns/0", args);

   var qq = context.Table.FromSqlInterpolated(fs).Where(m => ...);

当然,可以编写一个助手来为您执行此操作...

【讨论】:

【参考方案3】:

所以我找到了一种方法来实际查看 EF Core 如何解释插值查询的参数,并且能够使用它来获得我想要的结果。因此,以防万一有人想要或需要坚持纯插值查询,您可以根据需要使用以下模式:

WHERE MyTable.MyIntColumn::text LIKE ANY(Array[string.Join(",", myIntList).Split(",", StringSplitOptions.None)])

这也适用于文本值(只是不需要转换为文本),您可以使用如下通配符:

WHERE MyTable.MyStringColumn ILIKE ANY(ARRAY[("%" + string.Join("%,%", myStringList) + "%").Split(",", StringSplitOptions.None)])

【讨论】:

以上是关于如何比较 EF Core 插值查询中的整数列表的主要内容,如果未能解决你的问题,请参考以下文章

插值字符串上的 Ef Core vs Linq

为啥 EF Core 会生成倒位比较

如何在 EF 查询中执行日期比较?

如何在不使用 for 循环的情况下将列表中的所有项目与整数进行比较

bzoj4559 [JLOI2016]成绩比较 拉格朗日插值

向“datetime2”列添加值导致溢出。 EF Core 日期比较