Por que o operador Contains () prejudica tanto o desempenho do Entity Framewor
UPDATE 3: de acordo comeste anúncio, isso foi abordado pela equipe EF no EF6 alfa 2.
UPDATE 2: criei uma sugestão para corrigir esse problema. Para votar,go aqui.
onsidere um banco de dados SQL com uma tabela muito simple
CREATE TABLE Main (Id INT PRIMARY KEY)
Povo a tabela com 10.000 registro
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)
Criei um modelo EF para a tabela e execute a seguinte consulta no LINQPad (estou usando o modo "C # Statements" para que o LINQPad não crie um despejo automaticamente
var rows =
Main
.ToArray();
tempo de execução é de ~ 0,07 segundos. Agora, adiciono o operador Contains e execute novamente a consult
var ids = Main.Select(a => a.Id).ToArray();
var rows =
Main
.Where (a => ids.Contains(a.Id))
.ToArray();
O tempo de execução para este caso é 20,14 segundos (288 vezes mais lento)!
No começo, eu suspeitava que o T-SQL emitido para a consulta estava demorando mais para ser executado, então tentei recortá-lo e colá-lo do painel SQL do LINQPad no SQL Server Management Studi
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,...
E o resultado foi
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 88 ms.
Próximo Suspeitei que o LINQPad estivesse causando o problema, mas o desempenho é o mesmo, seja eu executado no LINQPad ou em um aplicativo de consol
ntão, parece que o problema está em algum lugar no Entity Framewor
Estou fazendo algo errado aqui? Esta é uma parte do meu código com tempo crítico, então há algo que posso fazer para acelerar o desempenho?
Estou usando o Entity Framework 4.1 e o Sql Server 2008 R
UPDATE 1:
Na discussão abaixo, houve algumas perguntas sobre se o atraso ocorreu enquanto a EF estava criando a consulta inicial ou enquanto analisava os dados que recebeu de volta. Para testar isso, executei o seguinte código,
var ids = Main.Select(a => a.Id).ToArray();
var rows =
(ObjectQuery<MainRow>)
Main
.Where (a => ids.Contains(a.Id));
var sql = rows.ToTraceString();
que força EF a gerar a consulta sem executá-la no banco de dados. O resultado foi que esse código exigia ~ 20 segundos para ser executado; portanto, parece que quase todo o tempo é gasto na criação da consulta inicia
CompiledQuery para o resgate então? Não é tão rápido ... O CompiledQuery exige que os parâmetros passados para a consulta sejam tipos fundamentais (int, string, float etc.). Ele não aceita matrizes ou IEnumerable, portanto, não posso usá-lo para uma lista de ID