Molto è stato detto in precedenza, ma tornando alle origini, in un modo più tecnico:
IEnumerable
è una raccolta di oggetti in memoria che è possibile enumerare , una sequenza in memoria che consente di scorrere attraverso (rende più semplice il foreach
ciclo all'interno , sebbene sia possibile utilizzarlo IEnumerator
solo). Risiedono nella memoria così com'è.
IQueryable
è un albero di espressioni che verrà tradotto in qualcos'altro ad un certo punto con la capacità di enumerare il risultato finale . Immagino che questo sia ciò che confonde la maggior parte delle persone.
Ovviamente hanno connotazioni diverse.
IQueryable
rappresenta un albero di espressioni (una query, semplicemente) che verrà tradotto in qualcos'altro dal provider di query sottostante non appena vengono chiamate API di rilascio, come le funzioni aggregate LINQ (Somma, Conteggio, ecc.) o ToList [Array, Dictionary ,. ..]. E IQueryable
anche gli oggetti implementano IEnumerable
, in IEnumerable<T>
modo che se rappresentano una query il risultato di quella query potrebbe essere ripetuto. Significa che IQueryable non deve essere solo query. Il termine giusto è che sono alberi di espressione .
Ora come vengono eseguite queste espressioni e ciò a cui si rivolgono dipende dai cosiddetti provider di query (esecutori di espressioni a cui possiamo pensare).
Nel mondo Entity Framework (ovvero il mistico fornitore di origine dati sottostante o il fornitore di query) le IQueryable
espressioni vengono tradotte in query T-SQL native . Nhibernate
fa cose simili con loro. Puoi scriverne uno tuo seguendo i concetti abbastanza ben descritti in LINQ: Creazione di un link Provider IQueryable , ad esempio, e potresti voler avere un'API di query personalizzata per il servizio del fornitore dell'archivio prodotti.
Quindi, fondamentalmente, gli IQueryable
oggetti vengono costruiti fino a quando non li rilasciamo esplicitamente e diciamo al sistema di riscriverli in SQL o altro e inviare la catena di esecuzione per l'elaborazione successiva.
Come se l' esecuzione differita sia una LINQ
funzione per contenere lo schema dell'albero delle espressioni nella memoria e inviarlo nell'esecuzione solo su richiesta, ogni volta che determinate API vengono chiamate contro la sequenza (lo stesso Count, ToList, ecc.).
L'uso corretto di entrambi dipende in larga misura dalle attività da affrontare per il caso specifico. Per il noto modello di repository, personalmente scelgo di tornare IList
, cioè IEnumerable
su Liste (indicizzatori e simili). Quindi è il mio consiglio di usare IQueryable
solo all'interno di repository e IEnumerable in qualsiasi altra parte del codice. Non dire delle preoccupazioni sulla testabilità che IQueryable
interrompono e rovinano il principio di separazione delle preoccupazioni . Se si restituisce un'espressione all'interno dei repository, i consumatori possono giocare con il livello di persistenza come desiderano.
Una piccola aggiunta al disordine :) (da una discussione nei commenti)) Nessuno di loro è un oggetto in memoria dato che non sono dei veri e propri tipi, sono dei marcatori di un tipo - se vuoi andare così in profondità. Ma ha senso (ed è per questo che anche MSDN lo dice in questo modo) pensare a IEnumerables come raccolte in memoria mentre IQueryables come alberi delle espressioni. Il punto è che l'interfaccia IQueryable eredita l'interfaccia IEnumerable in modo che se rappresenta una query, i risultati di quella query possano essere enumerati. L'enumerazione determina l'esecuzione dell'albero delle espressioni associato a un oggetto IQueryable. Quindi, in effetti, non puoi davvero chiamare nessun membro IEnumerable senza avere l'oggetto in memoria. Entrerà lì se lo fai, comunque, se non è vuoto. IQueryables sono solo query, non dati.