2015-10-15 30 views
12

Entity Framework 5+ deve precompilare tutte le query. Tuttavia, per le query comeEntity Framework: query precompilata per Enumerable.Contains

List<Guid> ids; 
var entities = context.MyEntities.Where(x => ids.Contains(x.Id)).ToArray(); 

Entity Framework non può precompilare l'interrogazione, e in funzione della complessità della query generale, l'analisi della struttura di espressione SQL può consumare diversi secondi. Qualcuno ha trovato una soluzione alternativa per ottenere comunque una query precompilata? Non capisco davvero perché sarebbe così difficile; naturalmente è difficile da fare con paramters, dal momento che il numero di elementi possono differire, ma sarebbe sufficiente avere SQL come

SELECT a, b, c from MyEntities 
WHERE c in __PLACEHOLDER__ 

e quindi di sostituire il segnaposto con gli elementi della lista effettivi. Ovviamente, non è bello come passare i parametri, ma sarebbe di gran lunga meglio che aspettare i secondi per analizzare l'intero albero delle espressioni più e più volte.

+0

Le liste non possono essere utilizzate come parametri in modo tale da fare ciò che suggerisci che è che costruisce una nuova query perché l'elenco può contenere elementi diversi ogni volta che viene chiamato. Quindi in realtà è una limitazione SQL. –

+0

Non del tutto; come detto, inizia ad analizzare ogni volta l'albero delle espressioni da zero.abbiamo una query con un paio di join che richiede 5 secondi per analizzare (con pochi ms su SQL Server), ecco perché sto cercando una soluzione alternativa. – Roland

+1

@rolandQuanto è grande questa lista? Hai anche da usare contiene? Il problema potrebbe dipendere da quanto tempo ogni voce è ....... solo quanto ha bisogno di confrontare. Ho avuto un problema simile con .StartsWith fino a quando non ho usato StringComparison.Ordinal lì che ha accelerato notevolmente (rispetto a .Contains). Il problema potrebbe essere solo che ha bisogno di scorrere attraverso stringhe troppo grandi (per grandi in termini di tempo richiesto). Se potessi cambiarlo in startswith e ordinal, questo dovrebbe spargere considerevolmente (ma dipende dal tuo caso d'uso esatto). – Thomas

risposta

6

È necessario innanzitutto capire come l'operatore "IN" lavora nella query SQL parametrizzata.

non funziona, parametro del comando SQL non accetta ARRAY come valore di parametro, anziché la query viene convertito

SELECT A FROM B WHERE C IN (@p1, @p2, @p3 ... etc) 

Questa interrogazione è numero variabile di parametri e questa ragione, v'è nessun modo per precompilare questa query con IEnumerable.Contains.

L'unica altra alternativa (long long way) è l'uso di Xml o Json (in arrivo in Sql 2016).

Salva il tuo IEnumerable come xml.

[10,20,20,50] can be translated to 
<data> 
    <int value="10"/> 
    <int value="20"/> 
    <int value="20"/> 
    <int value="50"/> 
</data> 

E si può quindi definire un VIEW con i parametri come

SELEZIONE A da B DOVE C IN (SELECT INT da XML (@ P1))

Ed è possibile utilizzare questa vista, però ci sono più sfide in EF su come attivare questa query, ma questa query può essere precompilata in quanto ha un solo parametro.

personalizzato SQL per prestazioni Hack

Per abbastanza semplice domanda come,

List<Guid> ids; 
var entities = context.MyEntities.Where(x => ids.Contains(x.Id)).ToArray(); 

potrebbe semplicemente utilizzare uno SQL personalizzata e fuoco,

var parameterList = ids.Select( 
    (x,i)=> new SqlCommandParameter(
     "@p"+i, x)); 

var pnames = String.Join(",", parameterList.Select(x=> x.ParameterName)); 

var entities = 
    context.SqlQuery<MyEntity>(
     "SELECT * FROM TABLE WHERE Id in (" + pnames + ")", 
     parameterList.ToArray()); 

tabella temporanea

È anche possibile utilizzare una tabella temporanea, ma ciò aumenta il numero di transazioni attive nel database.

+0

Grazie per questa risposta; purtroppo non è ancora quello che sto cercando. Come accennato in precedenza, abbiamo un sacco di query come questa, e scrivere un sacco di SQL/Views personalizzati non è davvero la strada da percorrere, altrimenti rovineremo la manutenibilità dell'applicazione. Mi chiedo se c'è un modo per aggiungere un tipo temporaneo a EF. Idea: potremmo aggiungere una tabella temporanea per memorizzare l'elenco dei parametri Contains e quindi introdurre un tipo temporaneo per formulare la query. Qualcuno sa se EF consente di introdurre tipi temporanei? – Roland

+0

Sì al posto di XML o JSON, è anche possibile utilizzare una tabella temporanea. ma aggiungerà un sovraccarico al Database, aumenterà il numero di transazioni attive, portando a scarse prestazioni dal motore del database. –

+0

Sì, ma posso aggiungere il tipo temporaneo C# a EF? Mi servirebbe per formulare la query, come context.MyEntities.Where (x => context.Set (typeof (TemporaryType)). Contains (x.Id)). Non mi dispiacerebbe fare tutto il riflesso necessario in background. – Roland