ОБНОВЛЕНИЕ 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<MainRow>)
Main
.Where (a => ids.Contains(a.Id));
var sql = rows.ToTraceString();
что заставляет EF генерировать запрос, не выполняя его в базе данных. В результате для выполнения этого кода потребовалось ~ 20 секунд, поэтому кажется, что почти все время уходит на построение начального запроса.
Тогда на помощь приходит CompiledQuery? Не так быстро ... CompiledQuery требует, чтобы параметры, передаваемые в запрос, были фундаментальными типами (int, string, float и т. Д.). Он не принимает массивы или IEnumerable, поэтому я не могу использовать его для списка идентификаторов.
var qry = Main.Where (a => ids.Contains(a.Id)); var rows = qry.ToArray();
увидеть, какая часть запроса требует времени?