Почему оператор Contains () так резко снижает производительность Entity Framework?

ОБНОВЛЕНИЕ 3: Согласно это объявление , это было решено командой EF в EF6 alpha 2.

ОБНОВЛЕНИЕ 2: Я создал предложение по устранению этой проблемы. Чтобы проголосовать за него, перейдите сюда .

Рассмотрим базу данных SQL с одной очень простой таблицей.

CREATE TABLE Main (Id INT PRIMARY KEY)

Я заполняю таблицу 10 000 записей.

WITH Numbers AS
(
  SELECT 1 AS Id
  UNION ALL
  SELECT Id + 1 AS Id FROM Numbers WHERE Id <= 10000
)
INSERT Main (Id)
SELECT Id FROM Numbers
OPTION (MAXRECURSION 0)

Я создаю EF-модель для таблицы и запускаю следующий запрос в LINQPad (я использую режим «C # Statements», поэтому LINQPad не создает дамп автоматически).

var rows = 
  Main
  .ToArray();

Время выполнения ~ 0,07 секунды. Теперь я добавляю оператор Contains и повторно запускаю запрос.

var ids = Main.Select(a => a.Id).ToArray();
var rows = 
  Main
  .Where (a => ids.Contains(a.Id))
  .ToArray();

Время выполнения для этого случая составляет 20,14 секунды (в 288 раз медленнее)!

Сначала я подозревал, что T-SQL, созданный для запроса, требует больше времени для выполнения, поэтому я попытался вырезать и вставить его из панели SQL LINQPad в SQL Server Management Studio.

SET NOCOUNT ON
SET STATISTICS TIME ON
SELECT 
[Extent1].[Id] AS [Id]
FROM [dbo].[Primary] AS [Extent1]
WHERE [Extent1].[Id] IN (1,2,3,4,5,6,7,8,...

Результат был

SQL Server Execution Times:
  CPU time = 0 ms,  elapsed time = 88 ms.

Затем я подозревал, что проблема связана с LINQPad, но производительность одинакова, независимо от того, запускаю ли я его в LINQPad или в консольном приложении.

Итак, похоже, проблема где-то в Entity Framework.

Я что-то здесь делаю не так? Это критичная по времени часть моего кода, поэтому могу ли я что-то сделать, чтобы повысить производительность?

Я использую Entity Framework 4.1 и Sql Server 2008 R2.

ОБНОВЛЕНИЕ 1:

В приведенном ниже обсуждении возникали некоторые вопросы о том, возникла ли задержка, когда EF строил начальный запрос или когда он анализировал полученные данные.Чтобы проверить это, я запустил следующий код

var ids = Main.Select(a => a.Id).ToArray();
var rows = 
  (ObjectQuery)
  Main
  .Where (a => ids.Contains(a.Id));
var sql = rows.ToTraceString();

, который заставляет EF генерировать запрос, не выполняя его в базе данных. В результате для выполнения этого кода потребовалось ~ 20 секунд, поэтому кажется, что почти все время уходит на построение начального запроса.

Значит, на помощь приходит CompiledQuery? Не так быстро ...CompiledQuery требует, чтобы параметры, передаваемые в запрос, были фундаментальными типами (int, string, float и т. Д.). Он не принимает массивы или IEnumerable, поэтому я не могу использовать его для списка идентификаторов.

79
задан Jacob 8 June 2019 в 08:39
поделиться