What is the difference between returning IQueryable
vs. IEnumerable
, when should one be preferred over the other?
I would like to clarify a few things due to seemingly conflicting responses (mostly surrounding IEnumerable).
(1) IQueryable
extends the IEnumerable
interface. (You can send an IQueryable
to something which expects IEnumerable
without error.)
(2) Both IQueryable
and IEnumerable
LINQ attempt lazy loading when iterating over the result set. (Note that implementation can be seen in interface extension methods for each type.)
In other words, IEnumerables
are not exclusively "in-memory". IQueryables
are not always executed on the database. IEnumerable
must load things into memory (once retrieved, possibly lazily) because it has no abstract data provider. IQueryables
rely on an abstract provider (like LINQ-to-SQL), although this could also be the .NET in-memory provider.
Sample use case
(a) Retrieve list of records as IQueryable
from EF context. (No records are in-memory.)
(b) Pass the IQueryable
to a view whose model is IEnumerable
. (Valid. IQueryable
extends IEnumerable
.)
(c) Iterate over and access the data set's records, child entities and properties from the view. (May cause exceptions!)
Possible Issues
(1) The IEnumerable
attempts lazy loading and your data context is expired. Exception thrown because provider is no longer available.
(2) Entity Framework entity proxies are enabled (the default), and you attempt to access a related (virtual) object with an expired data context. Same as (1).
(3) Multiple Active Result Sets (MARS). If you are iterating over the IEnumerable
in a foreach( var record in resultSet )
block and simultaneously attempt to access record.childEntity.childProperty
, you may end up with MARS due to lazy loading of both the data set and the relational entity. This will cause an exception if it is not enabled in your connection string.
Solution
Execute the query and store results by invoking resultList = resultSet.ToList()
This seems to be the most straightforward way of ensuring your entities are in-memory.
In cases where the you are accessing related entities, you may still require a data context. Either that, or you can disable entity proxies and explicitly Include
related entities from your DbSet
.