带有“Contains”的EF Core 7 Linq生成带有额外“or is not null”的SQL

x33g5p2x  于 2023-03-15  发布在  其他
关注(0)|答案(3)|浏览(273)

我最近更新了EF Core 6到7,现在查询变得比以前慢了很多,没有任何变化。看起来EF Core将LINQ查询转换为不同的SQL查询。
版本:

  • .NET:7个
  • EF核心:7.0.3

这是LINQ查询:

UPDATE:这是错误的,列表标签确实包含了null。这是完全不期望的,因为我们没有打开可空引用类型,所以我们没有更快地捕获它。

int userId = 3;
List<string> labels = new () { "one", "two", "three" };
List<RecordEntity> alreadySyncedEvents = await _dbContext.RecordEntities
    .IgnoreQueryFilters()
    .Where(x => x.UserId == userId)
    .Where(x => labels.Contains(x.Label))
    .Include(x => x.ExtraData)
    .ToListAsync();

以下是生成的SQL:

SELECT [a].[Id], [a].[Label], [a0].[Id], [a0].[ExtraData]
FROM [RecordEntities] AS [a]
LEFT JOIN [ExtraDataEntities] AS [a0] ON [a].[Id] = [a0].[RecordId]
WHERE ([a].[UserId] = 3) AND ([a].[Label] IN (N'one', N'two', N'three') OR ([a].[Label] IS NULL))
ORDER BY [a].[Id]

问题是OR ([a].[Label] IS NULL)导致此查询返回超过13k条记录,而在切换到EF Core 7之前,它返回1。它添加的唯一内容是额外的OR条件。
这是因为我没有使用可为空的引用类型,而这个字符串在默认情况下是可为空的吗?可为空的引用类型在这个项目中是完全禁用的。

bwntbbo3

bwntbbo31#

您可以添加额外条件

.Where(x => labels != null && labels.Contains(x.Label))

这似乎是一个bug,尽管您可能希望检查UseRelationalNulls(true)的配置,另请参见https://learn.microsoft.com/en-us/ef/core/querying/null-comparisons#using-relational-null-semantics

44u64gxh

44u64gxh2#

由于它们更改了EF Core 7中OR运算符的行为,因此需要显式检查空值。
使用||运算符将以下检查添加到第二个Where(),如下所示:

.Where(x => labels.Contains(x.Label) || x.Label == null)

它应该像老EF那样工作。

bihw5rsg

bihw5rsg3#

我发现问题了。
列表labels不应该包含空值,因为它是基于我们从另一个API(Microsoft Graph API)获得的列表,我们不应该期望空值。但由于一个bug,一个空值确实滑入其中。因此EF Core添加了一个OR ([a].[Label] IS NULL)。如果我显式过滤列表中的空值,查询将不再包含OR ([a].[Label] IS NULL)
我们开始在另一个分支中启用可空引用类型是一件好事,这样我们就可以更快地发现它。
这仍然是一个奇怪的巧合,这正好发生在我们切换到EF核心7的那一天。

相关问题