ОНОВЛЕННЯ 3: Згідно з цим оголошенням , це було розглянуто командою EF у EF6 alpha 2.
ОНОВЛЕННЯ 2: Я створив пропозицію щодо вирішення цієї проблеми. Щоб проголосувати за це, йди сюди .
Розглянемо базу даних SQL з однією дуже простою таблицею.
CREATE TABLE Main (Id INT PRIMARY KEY)
Я заповнюю таблицю 10000 записами.
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 #", щоб 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 вимагає, щоб параметри, що передаються у запит, були основними типами (int, string, float тощо). Він не приймає масиви або IEnumerable, тому я не можу використовувати його для списку ідентифікаторів.
var qry = Main.Where (a => ids.Contains(a.Id)); var rows = qry.ToArray();
зрозуміти, яка частина запиту займає час?