AKTUALIZACJA 3: Zgodnie z tym ogłoszeniem , zespół EF zajął się tym problemem w EF6 alfa 2.
UPDATE 2: Stworzyłem sugestię rozwiązania tego problemu. Aby zagłosować na to, przejdź tutaj .
Rozważmy bazę danych SQL z jedną bardzo prostą tabelą.
CREATE TABLE Main (Id INT PRIMARY KEY)
Zapełniam tabelę 10 000 rekordów.
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)
Buduję model EF dla tabeli i uruchamiam następującą kwerendę w LINQPad (używam trybu „C # Instrukcje”, więc LINQPad nie tworzy zrzutu automatycznie).
var rows =
Main
.ToArray();
Czas wykonania ~ 0,07 sekundy. Teraz dodaję operator Contains i ponownie uruchamiam zapytanie.
var ids = Main.Select(a => a.Id).ToArray();
var rows =
Main
.Where (a => ids.Contains(a.Id))
.ToArray();
Czas wykonania w tym przypadku to 20,14 sekundy (288 razy wolniej)!
Na początku podejrzewałem, że wykonanie T-SQL wyemitowanego dla zapytania trwa dłużej, więc próbowałem wyciąć go i wkleić z okienka SQL LINQPad do 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,...
Wynik był taki
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 88 ms.
Następnie podejrzewałem, że LINQPad powoduje problem, ale wydajność jest taka sama, niezależnie od tego, czy uruchamiam go w LINQPad, czy w aplikacji konsoli.
Wygląda więc na to, że problem tkwi gdzieś w Entity Framework.
Czy ja tu robię coś złego? To krytyczna czasowo część mojego kodu, więc czy jest coś, co mogę zrobić, aby przyspieszyć działanie?
Używam Entity Framework 4.1 i Sql Server 2008 R2.
AKTUALIZACJA 1:
W poniższej dyskusji pojawiło się kilka pytań dotyczących tego, czy opóźnienie wystąpiło podczas budowania przez EF zapytania początkowego, czy podczas analizowania otrzymanych danych. Aby to przetestować, uruchomiłem następujący kod,
var ids = Main.Select(a => a.Id).ToArray();
var rows =
(ObjectQuery<MainRow>)
Main
.Where (a => ids.Contains(a.Id));
var sql = rows.ToTraceString();
co zmusza EF do generowania zapytania bez wykonywania go w bazie danych. W rezultacie ten kod wymagał ~ 20 sekund do uruchomienia, więc wydaje się, że prawie cały czas zajmuje tworzenie początkowego zapytania.
CompiledQuery to ratunek? Nie tak szybko ... CompiledQuery wymaga, aby parametry przekazywane do zapytania były typami podstawowymi (int, string, float itd.). Nie akceptuje tablic ani IEnumerable, więc nie mogę go używać jako listy identyfikatorów.
var qry = Main.Where (a => ids.Contains(a.Id)); var rows = qry.ToArray();
sprawdzić, która część zapytania zajmuje trochę czasu?