此文章讲解了Entity Framework中IQueryable, IEnumerable, IList的区别,分享一下。数据库
使用Entity Framework等ORM框架的时候,SQL对于使用者来讲是透明的,每每不少人也不关心ORM所生成的SQL,然而系统出现性能问题的时候就必须关注生成的SQL以发现问题所在。express
使用过Toplink的朋友知道很只要设置日志打印级别=FINE就能够配置使之生成的SQL在服务器中打印出来,Entiry Framework没有那么幸运,在之前要检测生成SQL的惟一方法是SQL Server Profiler,但使用起来并不方便,结果也不能自动保存到文件中。缓存
Tracing and Caching Provider Wrappers for Entity Framework是Entity Framework Team新推出的开源SQL追踪和二级缓存的解决方案。原理是在负责执行具体SQL语句的data provider(SqlClient或者其余Client)之上插入了一层WrappingProvider,用于监控DbCommand.ExecuteReader(), ExecuteScalar() and ExecuteNonQuery(),将Sql命令输出到指定介质或者将查询结果缓存起来以重用。服务器
使用方法很简单,下载源代码编译后将dll添加到项目中,新加一个类WrappedNorthWindEntities继承原有的Entities便可,详见源代码中的示例。app
下面咱们使用EF Wrapper来监测Entify Framework中IQueryable, IEnumerable和IList所生成的SQL。框架
TestIQueryable() { (var ctx = WrappedNorthWindEntities()) { IQueryable<Product> expression = ctx.Products.Take(); IQueryable<Product> products = expression.Take(); Console.WriteLine(products.Count()); Console.WriteLine(products.Count()); (Product p products) { Console.WriteLine(p.ProductName); } (Product p products) { Console.WriteLine(p.ProductName); } } }
TestIEnumerable() { (var ctx = WrappedNorthWindEntities()) { IEnumerable<Product> expression = ctx.Products.Take().; IEnumerable<Product> products = expression.Take(); Console.WriteLine(products.Count()); Console.WriteLine(products.Count()); (Product p products) { Console.WriteLine(p.ProductName); } (Product p products) { Console.WriteLine(p.ProductName); } } }
TestIList() { (var ctx = WrappedNorthWindEntities()) { var expression = ctx.Products.Take(); IList<Product> products = expression.Take().; Console.WriteLine(products.Count()); Console.WriteLine(products.Count()); (Product p products) { Console.WriteLine(p.ProductName); } (Product p products) { Console.WriteLine(p.ProductName); } } }
IQueryable和IEnumerable都是延时执行(Deferred Execution)的,而IList是即时执行(Eager Execution)ide
IQueryable和IEnumerable在每次执行时都必须链接数据库读取,而IList读取一次后,之后各次都不需链接数据库。前二者很容易形成重复读取,性能低下,而且可能引起数据不一致性工具
IQueryable和IEnumerable的区别:IEnumberalb使用的是LINQ to Object方式,它会将AsEnumerable()时对应的全部记录都先加载到内存,而后在此基础上再执行后来的Query。因此上述TestIEnumerable例子中执行的SQL是"select top(5) ...",而后在内存中选择前两条记录返回。post
如下是一个IQueryable引起数据不一致性的例子:记录总数和记录详情二者本应一致,但因为IQueryable先后两次读取数据库,结果是现实有10条记录,却输出11条详情。
IQueryable<Product> products = ctx.Products.All(); count = products.Count(); Console.WriteLine(+count);
//此时另外一进程添加一个产品进数据库
(Product p products) { Console.WriteLine(p.ProductName); }
基于性能和数据一致性这两点,咱们使用IQueryable时必须谨慎,而在大多数状况下咱们应使用IList。
当你打算立刻使用查询后的结果(好比循环做逻辑处理或者填充到一个table/grid中),而且你不介意该查询会即时执行,使用ToList()
当你但愿查询后的结果能够供调用者(Consummer)做后续查询(好比这是一个"GetAll"的方法),或者你但愿该查询延时执行,使用AsQueryable()