Если мы передадим массив только с двумя значениями int[] ids = {1, 2}
в ваш метод GetEntities
EntityFramework сгенерирует следующий запрос:
SELECT
[Extent1].[Id] AS [Id],
...
FROM [dbo].[Entity] AS [Extent1]
WHERE ( NOT EXISTS (SELECT
1 AS [C1]
FROM (SELECT
1 AS [C0]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
UNION ALL
SELECT
1 AS [C0]
FROM ( SELECT 1 AS X ) AS [SingleRowTable2]) AS [UnionAll1]
)) OR (1 = [Extent1].[Id]) OR (2 = [Extent1].[Id])
Если мы увеличим количество элементов в массиве ids
, это запрос становится более сложным с большим количеством уровней вложенности. Я думаю, что EntityFramework использует некоторый рекурсивный алгоритм для генерации SQL -кода для !ids.Any()
выражения. Когда количество элементов в массиве ids
увеличивается, глубина рекурсии также увеличивается. Поэтому он генерирует StackOverflowException
, когда число элементов в массиве ids
(а также глубина рекурсии) велико.
Если мы удалим выражение !ids.Any()
, будет сгенерирован следующий запрос:
SELECT
[Extent1].[Id] AS [Id],
...
FROM [dbo].[Entity] AS [Extent1]
WHERE [Extent1].[Id] IN (1,2)
Такой запрос не генерирует StackOverflowException
, когда количество элементов в массиве ids
велико. Поэтому было бы лучше извлечь выражение !ids.Any()
из запроса LINQ:
public List<TEntity> GetEntities<TEntity>(int[] ids)
{
var someDbSet = new DbSet<TEntity>();
if (!ids.Any())
return someDbSet.ToList();
var resultQ = someDbSet.Where(t => ids.Contains(t.ID));
return resultQ.toList();
}
Следует также учитывать, что существует ограничение на количество элементов для условия WHERE IN
: Limit на условии WHERE col IN (...) .
ionutnespus писал:
Да, извлечение условия вне Where () работает. Тем не менее, я не смог найти никакого объяснения, почему EF будет использовать такой сложный алгоритм для такого простого условия. Есть какие-нибудь мысли по этому поводу?
Я решил ответить на этот вопрос, расширив этот пост, потому что ответ большой и содержит код.
Не знаю точно, почему EF генерирует такой сложный запрос, но я провел некоторое исследование, и вот мои мысли. Если мы изменим ваш метод GetEntites
и используем следующее условие в запросе LINQ:
someDbSet.Where(t => !ids.Any(i => i == 3) || ids.Contains(t.ID));
, будет сгенерирован следующий запрос SQL, если ids = {1, 2}
:
SELECT
[Extent1].[Id] AS [Id],
...
FROM [dbo].[Entity] AS [Extent1]
WHERE ( NOT EXISTS (
SELECT 1 AS [C1]
FROM (
SELECT 1 AS [C0] FROM ( SELECT 1 AS X ) AS [SingleRowTable1] WHERE 3 = 1
UNION ALL
SELECT 1 AS [C0] FROM ( SELECT 1 AS X ) AS [SingleRowTable2] WHERE 3 = 2
) AS [UnionAll1]
)) OR (1 = [Extent1].[Id]) OR (2 = [Extent1].[Id])
Здесь Вы можете видеть, что условие NOT EXISTS
содержит два подзапроса, каждый из которых проверяет, равен ли следующий элемент массива ids
требуемому значению. Я думаю, что логично использовать NOT EXISTS
SQL -условие для представления Any()
метода. Но почему EF генерирует один подзапрос для каждого элемента массива? По моему мнению, EF делает это потому, что EF Team пыталась написать алгоритм, который генерирует запросы, которые не зависят от типа базы данных. Но это только мое мнение. Может быть, лучше задать этот вопрос EF Team на github .