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 foreachciclo all'interno , sebbene sia possibile utilizzarlo IEnumeratorsolo). 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.
IQueryablerappresenta 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 IQueryableanche 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 IQueryableespressioni vengono tradotte in query T-SQL native . Nhibernatefa 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 IQueryableoggetti 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 LINQfunzione 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è IEnumerablesu Liste (indicizzatori e simili). Quindi è il mio consiglio di usare IQueryablesolo all'interno di repository e IEnumerable in qualsiasi altra parte del codice. Non dire delle preoccupazioni sulla testabilità che IQueryableinterrompono 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.