Entity Framework sembra sempre utilizzare le costanti nell'SQL generato per i valori forniti a Skip()
e Take()
.Force Entity Framework utilizza la parametrizzazione SQL per un migliore riutilizzo della cache proc SQL.
Nell'esempio ultra-semplificato di seguito:
int x = 10;
int y = 10;
var stuff = context.Users
.OrderBy(u => u.Id)
.Skip(x)
.Take(y)
.Select(u => u.Id)
.ToList();
x = 20;
var stuff2 = context.Users
.OrderBy(u => u.Id)
.Skip(x)
.Take(y)
.Select(u => u.Id)
.ToList();
codice precedente genera i seguenti query SQL:
SELECT TOP (10)
[Extent1].[Id] AS [Id]
FROM (SELECT [Extent1].[Id] AS [Id], row_number() OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
FROM [dbo].[User] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 10
ORDER BY [Extent1].[Id] ASC
SELECT TOP (10)
[Extent1].[Id] AS [Id]
FROM (SELECT [Extent1].[Id] AS [Id], row_number() OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
FROM [dbo].[User] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 20
ORDER BY [Extent1].[Id] ASC
Conseguente 2 piani Adhoc aggiunte alla cache SQL proc con 1 uso ciascun .
Quello che mi piacerebbe realizzare è per parametrizzare la logica Skip()
e Take()
quindi le seguenti query SQL vengono generati:
EXEC sp_executesql N'SELECT TOP (@p__linq__0)
[Extent1].[Id] AS [Id]
FROM (SELECT [Extent1].[Id] AS [Id], row_number() OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
FROM [dbo].[User] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > @p__linq__1
ORDER BY [Extent1].[Id] ASC',N'@p__linq__0 int,@p__linq__1 int',@p__linq__0=10,@p__linq__1=10
EXEC sp_executesql N'SELECT TOP (@p__linq__0)
[Extent1].[Id] AS [Id]
FROM (SELECT [Extent1].[Id] AS [Id], row_number() OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
FROM [dbo].[User] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > @p__linq__1
ORDER BY [Extent1].[Id] ASC',N'@p__linq__0 int,@p__linq__1 int',@p__linq__0=10,@p__linq__1=20
Questo si traduce in 1 piano preparato aggiunto alla cache SQL proc con 2 usi.
Ho alcune query abbastanza complesse e sto verificando un sovraccarico significativo (sul lato SQL Server) alla prima esecuzione e un'esecuzione molto più veloce nelle esecuzioni successive (dato che può utilizzare la cache del piano). Si noti che queste query più avanzate utilizzano già sp_executesql come altri valori sono parametrizzati, quindi non sono interessato a tale aspetto.
La prima serie di query generate sopra sostanzialmente significa che qualsiasi logica di impaginazione creerà una nuova voce nella cache del piano per ogni pagina, espandendo la cache e richiedendo l'overhead di generazione del piano da sostenere per ciascuna pagina.
Posso forzare Entity Framework per parametrizzare i valori? Ho notato altri valori, ad es. nelle clausole Where
, a volte parametrizza i valori e talvolta utilizza costanti.
Sono completamente fuori a pranzo? C'è qualche ragione per cui il comportamento esistente di Entity Framework è migliore del comportamento che desidero?
Edit: Nel caso in cui sia rilevante, devo dire che sto utilizzando Entity Framework 4.2.
Edit 2: Questa domanda non è un duplicato di Entity Framework/Linq to SQL: Skip & Take, che chiede semplicemente come garantire che Skip
e Take
eseguire in SQL invece che sul client. Questa domanda riguarda la parametrizzazione di questi valori.
Questo collegamento spiega come utilizzare Linq con SQL Params sarà necessario scorrere verso il basso fino alla fine del collegamento per vedere la spiegazione e il sito di esempio LinqPad - http://www.linqpad.net/WhyLINQBeatsSQL.aspx – MethodMan
Grande osservazione. Normalmente non utilizzo EF per progetti "reali", semplicemente giocando con piccole cose, e non ho mai notato questo comportamento prima. Se EF non sta parametrizzando tutto ciò che può, allora considero un enorme difetto. – CodingWithSpike
ottima domanda - penseresti che fossero ottimizzati per il possibile riutilizzo dei piani – BrokenGlass