Comprensione .Senumerable () in Linq a SQL
-
27-09-2020 - |
Domanda
Dato la seguente query LINQ a SQL:
var test = from i in Imports
where i.IsActive
select i;
.
L'istruzione SQL interpretata è:
SELECT [t0].[id] AS [Id] .... FROM [Imports] AS [t0] WHERE [t0].[isActive] = 1
.
Dì che volevo eseguire qualche azione nella selezione che non può essere convertita in SQL.È la mia comprensione che il modo convenzionale per realizzare questo è quello di fare AsEnumerable()
convertendolo così in un oggetto praticabile.
Dato questo codice aggiornato:
var test = from i in Imports.AsEnumerable()
where i.IsActive
select new
{
// Make some method call
};
.
e aggiornato SQL:
SELECT [t0].[id] AS [Id] ... FROM [Imports] AS [t0]
.
Avvia la mancanza di una clausola WHERE nell'istruzione SQL eseguita.
Significa che l'intera tabella "importazioni" è memorizzata nella memorizzazione nella memoria? Questa prestazione lenta se il tavolo contenesse una grande quantità di record?
Aiutami a capire cosa sta effettivamente accadere dietro le quinte qui.
Soluzione
Il motivo per Asenumerable è
.Asenumerable (TSOURCE) (IIMUMACERARE (TSOURCE)) può essere usato per scegliere tra query implementazioni quando una sequenza implementa ienumerable (t) ma ha anche Un insieme diverso di query pubblica Metodi disponibili
Quindi quando si stava chiamando il metodo Where
prima, stavi chiamando un metodo Where
diverso da IEnumerable.Where
. Quell'istruzione Where
era per LINQ da convertire in SQL, il nuovo Where
è il IEnumerable
che assume un IEnumerable
, enumeralo e produce gli elementi corrispondenti. Il che spiega perché vedi il diverso sql che viene generato. La tabella verrà presa per intero dal database prima che l'estensione Where
verrà applicata nella seconda versione del codice. Questo potrebbe creare un collo di bottiglia serio, perché l'intera tabella deve essere in memoria, o peggiore l'intera tabella dovrebbe viaggiare tra i server. Consenti SQL Server di eseguire il Where
e fare ciò che fa meglio.
Altri suggerimenti
Nel punto in cui l'enumerazione viene elencata attraverso, il database verrà quindi interrogato e l'intero Ristorante ha recuperato.
Una soluzione a part-and-part può essere la strada.Considera
var res = (
from result in SomeSource
where DatabaseConvertableCriterion(result)
&& NonDatabaseConvertableCriterion(result)
select new {result.A, result.B}
);
.
Diciamo anche che non hatabaseconvertablecreditore richiede il campo C dal risultato.Poiché NondatabaseConvertableCriterion fa ciò che suggerisce il suo nome, questo deve essere eseguito come un'enumerazione.Tuttavia, considera:
var partWay =
(
from result in SomeSource
where DatabaseConvertableCriterion(result)
select new {result.A, result.B, result.C}
);
var res =
(
from result in partWay.AsEnumerable()
where NonDatabaseConvertableCriterion select new {result.A, result.B}
);
.
In questo caso, quando la risoluzione è enumerata, interrogata o altrimenti utilizzata, il più lavoro possibile sarà passato al database, che ritornerà abbastanza per continuare il lavoro.Supponendo che sia davvero davvero impossibile riscrivere in modo che tutto il lavoro possa essere inviato al database, questo potrebbe essere un compromesso adatto.
Ci sono tre implementazioni di AsEnumerable
.
DataTableExtensions.AsEnumerable
Estende un DataTable
per dargli un'interfaccia IEnumerable
in modo da poter utilizzare LINQ contro il DataTable
.
Enumerable.AsEnumerable<TSource>
e ParallelEnumerable.AsEnumerable<TSource>
< / Strong>
.Il metodo
AsEnumerable<TSource>(IEnumerable<TSource>)
non ha alcun effetto diverso da quello di modificare il tipo di sorgente di compilazione da un tipo implementaIEnumerable<T>
aIEnumerable<T>
stesso.
AsEnumerable<TSource>(IEnumerable<TSource>)
può essere utilizzato per scegliere Tra le implementazioni della query quando una sequenza implementaIEnumerable<T>
ma ha anche un insieme diverso di metodi di query pubblica a disposizione. Ad esempio, dato un generatore di classe genericoTable
che implementaIEnumerable<T>
e ha i propri metodi comeWhere
,Select
eSelectMany
, una chiamata aWhere
invocherebbe il metodo delWhere
pubblicoTable
. Un tipoTable
che rappresenta una tabella di database potrebbe avere un MetodoWhere
che prende l'argomento predicato come un albero di espressione e converte l'albero su SQL per l'esecuzione remota. Se l'esecuzione remota non è desiderato, ad esempio perché il predicato invoca un locale Metodo, il metodoAsEnumerable<TSource>
può essere utilizzato per nascondere il Metodi personalizzati e invece rendono gli operatori di query standard Disponibile.
in altre parole.
Se ho un
IQueryable<X> sequence = ...;
.
Da un LinqProvider, come il framework di entità e lo faccio,
sequence.Where(x => SomeUnusualPredicate(x));
.
Quella query sarà composta e eseguita sul server. Questo fallirà in fase di esecuzione perché l'entityframework non sa come convertire SomeUnusualPredicate
in SQL.
Se voglio che eseguire la dichiarazione con LINQ agli oggetti invece, lo faccio,
sequence.AsEnumerable().Where(x => SomeUnusualPredicate(x));
.
Ora il server restituirà tutti i dati e verranno utilizzati Enumerable.Where
da LINQ a Oggetti al posto dell'implementazione del provider di query.
Non importa che il framework di entità non sappia interpretare SomeUnusualPredicate
, la mia funzione verrà utilizzata direttamente. (Tuttavia, questo può essere un approccio inefficiente poiché tutte le righe verranno restituite dal server.)
Credo che l'Asenumeracraffracracraffracracraffracracraffracrable debba comunicare al compilatore che metodi di estensione da utilizzare (in questo caso quelli definiti per Ienumerable invece di quelli per iqueryable). L'esecuzione della query è ancora rinviata finché non si chiamano torre o enumerano su di esso.