【问题标题】:EF Core generating reverse queryEF Core 生成反向查询
【发布时间】:2019-02-07 11:18:57
【问题描述】:

有4张桌子:

上传日期

Id 
Description

上传类型

Id 
Description

上传状态

Id 
Description

上传详情

Id
UploadDateId (FK)
UploadTypeId (FK)
UploadStatusId (FK)
OtherFields..

上传日期 (数据)

1    Aug-2018
2    Sep-2018
3    Oct-2018
4    Nov-2018
5    Dec-2018
6    Jan-2019

UplodeType (数据)

1    Partner
2    Retail
3    Customer

UplodeStatus (数据)

1    Uploaded
2    Processing
3    Successful

UplodeDetail (数据)

Id    UploadDateId     UploadTypeId    UploadStatusId    other fields
1     1                1               3                 ...
2     1                2               3                 ...
3     2                2               3                 ...
4     2                1               3                 ...
5     1                3               3                 ...
6     2                3               2                 ...
7     3                2               1                 ...
8     4                2               1                 ...
9     4                2               3                 ...

我正在尝试做的事情是获取所有上传类型

上传成功的月份

查询

var list = await _iContext.UploadDate.Where(e => e.UploadDetails.All(o => o.UploadStatusId == (byte)EnumType.UploadStats.Successful)).Distinct().ToListAsync();

所以,从UploadDate 我得到UploadDetails 中的所有条目都成功的地方。它应该给我Aug-2018。但它给了Dec-2018Jan-2019

我签入了SQL Profiler,它正在生成以下查询...

SELECT DISTINCT [e].[Id], [e].[Description]
FROM [UploadDate] AS [e]
WHERE NOT EXISTS (
    SELECT 1
    FROM [UploadDetail] AS [o]
    WHERE ([e].[Id] = [o].[UploadDateId]) AND ([o].[UploadStatusId] <> CAST(3 AS tinyint)))

基本上过滤掉 NOT 成功的所有内容,从技术上讲,这就是我希望它生成的 reverse,类似于...

SELECT DISTINCT [e].[Id], [e].[Description]
FROM [UploadDate] AS [e]
WHERE EXISTS (
    SELECT 1
    FROM [UploadDetail] AS [o]
    WHERE ([e].[Id] = [o].[UploadDateId]) AND ([o].[UploadStatusId] = CAST(3 AS tinyint))).

另外,如果我运行上述查询(就在上面,不是EF Core 生成的查询,我会得到Aug-2018,这是预期的结果。

那么,为什么EF Core 生成的查询与我打算写的相反?还是我自己写了一个完全错误的查询?

【问题讨论】:

  • EF Core 生成的查询对于 All 运算符是正确的(您的等效于 Any 并将匹配 [1,2,4])。它给你 [1,5,6],不是吗?问题是包含 5,6?

标签: c# sql-server entity-framework ef-core-2.0


【解决方案1】:

两个查询都返回不正确的结果。

EF Core 生成的 SQL 查询返回 [1, 5, 6]。

手写 SQL 查询(相当于使用 Any 而不是 All 并且如果您这样做也将由 EF Core 生成)正在返回 [1, 2, 4]。

而想要的结果是 [1]。

首先,众所周知

All(condition)

与(等价于)相同

!Any(!condition)

第二个事实是(很容易看出)当序列为空(没有元素)时,两个表达式都返回true。这在技术上是正确的 - 所有(在这种情况下为零)元素都符合条件。或者没有不符合条件的元素。

但在您的情况下不起作用,因为您真正想要的是“获取上传存在的月份并且上传对于所有上传成功types",表示为:

.Where(e => e.UploadDetails.Any()
    && e.UploadDetails.All(o => o.UploadStatusId == 3))

或“存在上传成功,不存在上传失败”,表示为:

.Where(e => e.UploadDetails.Any(o => o.UploadStatusId == 3)
    && !e.UploadDetails.Any(o => o.UploadStatusId != 3))

这两种情况都会产生所需的行为。但是,它们将生成 2 个相关的子查询来执行检查。

如果您只想使用一个相关子查询执行检查(这不能保证查询会更快 - 需要测量),您可以使用以下技巧:

.Where(e => e.UploadDetails.Min(o => o.UploadStatusId == 3 ? 1 : (int?)0) == 1)

它利用Min&lt;int?&gt; 函数在序列没有元素时返回null 的事实。这一点,再加上内部的条件逻辑,确保只有当有匹配条件的元素并且没有不匹配的元素时才会返回1。这正是我们所需要的。

【讨论】:

    猜你喜欢
    • 2017-12-16
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 2020-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多