EF Core生成反向查询

时间:2019-02-07 11:18:58

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

有4个表:

上传日期

Id 
Description

上传类型

Id 
Description

UploadStatus

Id 
Description

UploadDetail

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

UplodeDate (数据)

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生成与我打算写的相反的查询?还是我自己编写了完全错误的查询?

1 个答案:

答案 0 :(得分:1)

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

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

手写SQL查询(等效于使用Any而不是All,如果这样做的话,它也会由EF Core生成)返回[1、2、4]

期望的结果是[1]。

首先,这是一个众所周知的事实

All(condition)

与(等同于)

!Any(!condition)

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

但是在您的情况下不起作用,因为您真正想要的是“获取存在上传并且成功上传全部的月份类型” ,表示为:

.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<int?>函数将返回null。这加上内部的条件逻辑可确保仅当存在匹配条件的元素且没有不匹配条件的元素时,它才返回1。正是我们需要的。