Entity Framework Core 8Where IN 与Where IN OPENJSON

Jam*_*ill 7 c# entity-framework-core sql-server-2017 ef-core-8.0

WHERE INEF Core 8 现在使用和 的组合将值内联到 where 语句中,OPENJSON而不是以前的WHERE IN(...).

文档中记录了此更改,所述原因如下:

此处值的内联是以不存在 SQL 注入攻击的机会的方式完成的。下面描述的使用 JSON 的更改完全与性能有关,与安全性无关。

不幸的是,OPENJSON我们 2017 年 SQL Server 实例的性能很差。

下面的查询由 EF Core 8 生成,运行时间为1.8 秒,产生近400,000 次读取

DECLARE @__scheduleTagIds_0 nvarchar(4000) = N'[5835,5970,6563,6564,6565,6645,6835,6850,7034,7127]';

SELECT  [s].[ScheduleTagId]
       ,[s].[MustStartProdBy]
FROM    [ScheduleTagMustStartBy] AS [s]
WHERE   [s].[ScheduleTagId] IN (
    SELECT  [s0].[value]
    FROM    OPENJSON(@__scheduleTagIds_0) WITH ([value] int '$') AS [s0]
)
Run Code Online (Sandbox Code Playgroud)

如果我重构查询以使用标准WHERE IN(...),执行时间将降至120 毫秒29,000 次读取

SELECT  [s].[ScheduleTagId]
       ,[s].[MustStartProdBy]
FROM    [ScheduleTagMustStartBy] AS [s]
WHERE   [s].[ScheduleTagId] IN (5835,5970,6563,6564,6565,6645,6835,6850,7034,7127)
Run Code Online (Sandbox Code Playgroud)

我的应用程序中有数百个查询在使用.Where(x => [collection].Contains(x.Id)),我非常担心在选择查询中看到的性能下降。

问题

我可以做什么来缓解这个问题?我对 EF 或 SQL Server 端的选项持开放态度(尽管不想更改数据库的兼容性级别)。

Mar*_*ith 1

根据文档,防止使用 OPENJSON(... 的唯一方法是将数据库兼容性级别更改为 120(低于 SQL Server 2016)。这不是一个理想的解决方案。是否有其他方法来限制/阻止使用 OPENJSON?

您不必将数据库兼容级别更改为 120。您可以调用UseCompatibilityLevel(120)告诉 EF 生成旧式 SQL,无论兼容级别实际设置为多少。

还有一个选项可以避免这种全局设置,而只是恒定化有问题的特定查询。但我认为这种EF.Constant方法尚未进入正式版本,因此您需要使用每日构建。

SQL Server 上是否存在可以提高 OPENJSON 性能的配置?从分析器数据中可以清楚地看出,SQL Server 在使用 OPENJSON 时无法正确使用索引。

它当然可以使用索引,OPENJSON只是可能选择不这样做。

当你传递常量时它可以确定

  • 您传递了多少物品?
  • 您传递了多少重复项?如果有的话?
  • 您传递的确切值是多少?(如果你有倾斜的基数,这可能很有用)

由于OPENJSON它无法说出任何内容,因此只能靠猜测。这可能会导致不同的(更糟糕的)计划。

  • 感谢您总结所有这些。自从有人在评论中分享这个问题以来,我一直在 github 上关注这个问题。EF 团队的倒退令人震惊。我希望他们尽快处理。 (2认同)