zoukankan      html  css  js  c++  java
  • EF入门 IQueryable和IEnumberable的区别

    IEnumerable接口

    公开枚举器,该枚举器支持在指定类型的集合上进行简单迭代。也就是说:实现了此接口的object,就可以直接使用foreach遍历此object;

    IQueryable 接口

    它继承 IEnumerable 接口,而因为.net版本加入Linq和IQueryable后,使得IEnumerable不再那么单调,变得更加强大和丰富。

    为了区别两个接口,我们通过一个实际的例子来解释一下。

    编写如下代码:

    static void Main(string[] args)
            {
                //创建数据库访问网关
                using (SchoolDBEntities schoolEntities = new SchoolDBEntities())
                {
                    //查询的结果放入IQueryable接口的集合中
                    IQueryable<T_Class> classesIQue = (from c in schoolEntities.T_Class
                                                       orderby c.ID
                                                         select c).Skip<T_Class>(3).Take<T_Class>(3);
                    //注意这个AsEnumerable<T_Class>()在分页查询之前,先将其转换成IEnumerable类型
                    IEnumerable<T_Class> classesIEnu = (from c in schoolEntities.T_Class
                                                        orderby c.ID   
                                                        select c).AsEnumerable<T_Class>().Skip<T_Class>(3).Take<T_Class>(3);
                    //因为启用了延迟加载机制,所以下面调用一下,才会真正去读取数据库
                    int i = 0;
                    foreach (var c in classesIQue)
                    {
                        i++;
                    }
                    Console.WriteLine(i);
                    foreach (var c in classesIEnu)
                    {
                        i++;
                    }
                    Console.WriteLine(i);
                }
                Console.WriteLine("OK");
                Console.ReadKey();
            }

    注意红色代码部分,在用linq查询实体集合之前我先将其转换成  IEnumerable接口类型,看看最终执行的sql是怎样的。

    第一种:直接返回 IQueryable类型的查询,如下图所示:

    第二种:在用分页查询之前先将其转换成 IEnumerable实际执行的sql如下图所示:

    总结

    IQueryable接口与IEnumberable接口的区别:  IEnumerable<T> 泛型类在调用自己的SKip 和 Take 等扩展方法之前数据就已经加载在本地内存里了,而IQueryable<T> 是将Skip ,take 这些方法表达式翻译成T-SQL语句之后再向SQL服务器发送命令,它并不是把所有数据都加载到内存里来才进行条件过滤。

    以下转载地址  http://www.cnblogs.com/hiteddy/archive/2011/10/01/Difference_among_IQueryable_IEnumeralb_IList_in_Entity_Framework.html

    使用工具追踪EF生成的SQL

    使用Entity Framework等ORM框架的时候,SQL对于使用者来说是透明的,往往很多人也不关心ORM所生成的SQL,然而系统出现性能问题的时候就必须关注生成的SQL以发现问题所在。

    使用过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即可,详见源代码中的示例。

    测试IQueryable, IEnumerable, IList的区别

    下面我们使用EF Wrapper来监测Entify Framework中IQueryable, IEnumerable和IList所生成的SQL。

    private static void TestIQueryable()
    {
        using (var ctx = new WrappedNorthWindEntities())
        {
            IQueryable<Product> expression = ctx.Products.Take(5);
            IQueryable<Product> products = expression.Take(2);   // A  不执行SQL
            Console.WriteLine(products.Count());          // B SELECT COUNT(1) FROM ( SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products] ))
            Console.WriteLine(products.Count());          // C  SELECT COUNT(1) FROM ( SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products] ))
            foreach (Product p in products)             // D  SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products] 
            {
                Console.WriteLine(p.ProductName);
            }
            foreach (Product p in products)              // E  SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products] )
            {
                Console.WriteLine(p.ProductName);
            }
        }
    }
    private static void TestIEnumerable()
    {
        using (var ctx = new WrappedNorthWindEntities())
        {
            IEnumerable<Product> expression = ctx.Products.Take(5).AsEnumerable();
            IEnumerable<Product> products = expression.Take(2);  // A  不执行SQL
            Console.WriteLine(products.Count());          // B SELECT TOP (5) * FROM [dbo].[Products]
            Console.WriteLine(products.Count());          // C  SELECT TOP (5) * FROM [dbo].[Products]
            foreach (Product p in products)             // D  SELECT TOP (5) * FROM [dbo].[Products] 
            {
                Console.WriteLine(p.ProductName);
            }
            foreach (Product p in products)              // E  SELECT TOP (5) * FROM [dbo].[Products]
            {
                Console.WriteLine(p.ProductName);
            }
        }
    }
    private static void TestIList()
    {
        using (var ctx = new WrappedNorthWindEntities())
        {
            var expression = ctx.Products.Take(5);
            IList<Product> products = expression.Take(2).ToList(); // A  SELECT TOP (2) * FROM ( SELECT TOP (5) * FROM [dbo].[Products]
    
            Console.WriteLine(products.Count());            // B 不执行SQL
            Console.WriteLine(products.Count());            // C  不执行SQL
            foreach (Product p in products)               // D  不执行SQL
            {
                Console.WriteLine(p.ProductName);
            }
            foreach (Product p in products)                // E  不执行SQL
            {
                Console.WriteLine(p.ProductName);
            }
        }
    }

    测试结果

    1. IQueryable和IEnumerable都是延时执行(Deferred Execution)的,而IList是即时执行(Eager Execution)
    2. IQueryable和IEnumerable在每次执行时都必须连接数据库读取,而IList读取一次后,以后各次都不需连接数据库。前两者很容易造成重复读取,性能低下,并且可能引发数据不一致性
    3. IQueryable和IEnumerable的区别:IEnumberalb使用的是LINQ to Object方式,它会将AsEnumerable()时对应的所有记录都先加载到内存,然后在此基础上再执行后来的Query。所以上述TestIEnumerable例子中执行的SQL是"select top(5) ...",然后在内存中选择前两条记录返回。

    以下是一个IQueryable引发数据不一致性的例子:记录总数和记录详情两者本应一致,但由于IQueryable前后两次读取数据库,结果是现实有10条记录,却输出11条详情。

        IQueryable<Product> products = ctx.Products.All();
        //开始的时候数据库product表中有10条记录, count = 10
        int count = products.Count();
        Console.WriteLine("Count of products:"+count);  
            
        //此时另一进程添加一个产品进数据库
        //会重新读取数据库并输出11个产品名称
        foreach (Product p in products)       
        {
        Console.WriteLine(p.ProductName);    
        }

    结论

    基于性能和数据一致性这两点,我们使用IQueryable时必须谨慎,而在大多数情况下我们应使用IList。

    • 当你打算马上使用查询后的结果(比如循环作逻辑处理或者填充到一个table/grid中),并且你不介意该查询会即时执行,使用ToList()
    • 当你希望查询后的结果可以供调用者(Consummer)作后续查询(比如这是一个"GetAll"的方法),或者你希望该查询延时执行,使用AsQueryable()
  • 相关阅读:
    第4月第1天 makefile automake
    第3月30天 UIImage imageWithContentsOfFile卡顿 Can't add self as subview MPMoviePlayerControlle rcrash
    第3月第27天 uitableviewcell复用
    learning uboot fstype command
    learning uboot part command
    linux command dialog
    linux command curl and sha256sum implement download verification package
    learning shell script prompt to run with superuser privileges (4)
    learning shell get script absolute path (3)
    learning shell args handing key=value example (2)
  • 原文地址:https://www.cnblogs.com/wphl-27/p/5899254.html
Copyright © 2011-2022 走看看