Returning IEnumerable vs. IQueryable

前端 未结 14 2512
梦毁少年i
梦毁少年i 2020-11-21 22:59

What is the difference between returning IQueryable vs. IEnumerable, when should one be preferred over the other?



        
14条回答
  •  别那么骄傲
    2020-11-21 23:40

    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

    • I have found that enabling MARS in the connection string works unreliably. I suggest you avoid MARS unless it is well-understood and explicitly desired.

    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.

提交回复
热议问题