EF ICollection、List、IEnumerable和IQueryable的区别

56

因此,我的EF模型具有关系,根据我在示例中看到的,这些关系应该使用ICollection的虚拟属性完成。

示例:

 public class Task
    {
        public int Id { get; set; }
        public string Description { get; set; }
        public virtual ICollection<SubTask>  { get; set; }
    }

我在某处读到应该使用IEnumerable来防止延迟执行,这是正确的吗?这意味着如果我的DAL方法返回IEnumerable而不是IQueryable,那么SQL将在那个时刻执行,而不是在我在Web页面中调用.ToList时执行。

那么,最佳实践是什么? 我应该返回什么? IEnumerable,List? IList,ICollection?

谢谢


我曾认为virtual关键字决定了执行是否被延迟,但我可能是错的。 - mfussenegger
1
@mfussenegger virtual 只用于可以被覆盖的声明。 - Chris Klepeis
2个回答

75

IQueryable

  • 只有在迭代项目时才会执行查询,例如使用.ToList()foreach。这意味着您仍然可以添加过滤器,例如Where()
  • 扩展IEnumerable接口。

IEnumerable

  • 正向遍历列表中的项。如果不传递前面的项,则无法获取“第4项”。
  • 只读列表,您不能添加或删除其中的内容。
  • 仍可能使用延迟执行(IQueryable仍是IEnumerable)。

IList

  • 完全随机访问完整列表。
  • 可能完全在内存中(没有延迟执行,但不知道实现此操作的确切类是什么)。
  • 支持添加和删除。
  • 扩展IEnumerable和ICollection接口。

ICollection

  • 介于IEnumerable和IList之间。
  • 扩展IEnumerable接口。

"最佳"取决于您的要求。通常,如果您只想显示项目,则IEnumerable“已经足够好了”。至少始终使用泛型变量。


4
如果将导航属性公开为IEnumerable<T>,则无法向集合中添加项目。 - Kristof Claes
4
很好的解析。关于延迟执行,只有在调用IEnumerable(其他类型都是从它派生而来)上的.GetEnumerator时才会形成查询。如果使用除IQueryable之外的任何东西,所有结果将传输到客户端并在那里进行过滤,而不是在数据库层面应用过滤器。您需要使用IQueryable来保留表达式树,以便GetEnumerator使用它来生成适当的TSQL。 - Jim Wooley
1
使用 IEnumerable 时会对性能产生什么影响?一旦类型被视为 IEnumerable,所有针对列表和数组的 LINQ 优化都不再适用,现在我们需要考虑委托等其他因素。 - Schultz9999
2
@Schultz9999 - 大多数LINQ-to-Objects方法适用于IEnumerables,而不是ILists。它们可能会在内部检查它是否也是IList或ICollection,并使用优化的代码。还可以参考Jon Skeet的EduLinq系列文章。 - Hans Kesting
我认为IList不能保证在内存中,因为它不是一个具体的类型。 - Gusdor
在实体上使用IQueryable的用例是什么? - Wouter

3
在EF中,IEnumerable的一个区别是只有当你真正枚举列表时,它才会执行到数据库的查询。而IList会执行查询并将项目提取到内存中。我在缓存方面遇到了奇怪的行为。缓存IEnumerable不会存储项目,而是未获取的枚举,每次调用缓存并获取枚举以某种目的时,它都会去DB并执行查询,因此缓存是无用的。但是,在枚举上使用ToList()方法可以获取项目并将其存储在缓存中,因此仅需要1次访问数据库。在这篇文章中了解更多:http://www.dailycode.info/BlogV2/post/entityframework-adding-ienumerable-to-cache

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接