EF Core генерирует обратный запрос - PullRequest
0 голосов
/ 07 февраля 2019

Есть 4 таблицы:

UploadDate

Id 
Description

UploadType

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-2018 и Jan-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 голосов
/ 07 февраля 2019

Оба запроса возвращают неверные результаты.

Сгенерированный 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 только тогда, когда есть элементы, соответствующие условию, и нет элементов, не соответствующих ему.Что именно то, что нам нужно.

...