ОБНОВЛЕНИЕ 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, поэтому я не могу использовать его для списка идентификаторов.