其他分享
首页 > 其他分享> > IQueryable 和 IEnumerable 的区别

IQueryable 和 IEnumerable 的区别

作者:互联网

 

来源:https://blog.guoqianfan.com/2019/11/17/distinguish-between-IQueryable-and-IEnumerable-in-CSharp/

前言

不管是Linq to object,还是Linq to sql或Linq to Entity,IQueryableIEnumerable都是延迟执行的,它们之间的区别仅仅在于扩展方法的参数类型不同。(迭代/枚举方式不同?作用对象不同?)

IQueryable 和 IEnumerable 的区别

AsEnumerable() 和 ToList() 的区别

IQueryable对象使用AsEnumerable()后,仍然是延迟执行,不过此时对象本质已经变了。

前面已经说了 IEnumerable的扩展方法接受的是Func(C#语法),当ie对象(iq转变) 真正使用时,会有2个步骤:

  1. 它会把iq对象(转变之前的) 的扩展方法翻译成sql语句,查询出数据加载到内存中,变为ie对象;
  2. 此时再把ie对象(转变之后的) 的扩展方法,使用C#求解,得到最终结果。

例如:

iq对象的Skip、Take方法,会被翻译成sql,在数据库里执行取出最终结果。

而ie对象的Skip、Take方法,则会取出全部数据到内存中,在内存中执行Skip、Take,会耗费大量资源。

使用场景

不过为了省事,我一般都是使用IQueryable拼接好条件后,直接ToList()来使用了。。。

备注

异常:System.InvalidOperationException: 无法枚举查询结果多次

异常出现条件

在Linq to sql或EF(非EFCore)中,直接执行sql语句来查询数据后,对数据集(IEnumerable)进行多次枚举操作就会引发这个异常。

经测试,多次Count()会引发此异常。其他的Sum()foreach等等应该也是,有待验证。。。

我的理解是:数据集(IEnumerable)是使用枚举器来处理每项数据的,而枚举器只能走一次。 搞不懂枚举器和迭代器了,需要研究下。。。

注意,EFCore中不会出现这个异常,原因请搜索efcore执行sql

解决方法

异常重现代码

下面的代码是Linq to sql的,网上说EF也会出现该异常,代码应该类似。

另外网上搜索该异常大部分都是执行存储过程时出现的,其实也是直接执行sql来查询数据,本质一样。

string sql = @"
select top 100 *
from [dbo].[BaseSupplier_OTAOnline]";

//return db.ExecuteQuery<T>(sql, parameters);
IEnumerable<BaseSupplier_OTAOnline> ieBs02 = bdb.QueryBySql<BaseSupplier_OTAOnline>(sql);//"exec Pro_BaseSpOtaOnline_Test01"

int count = ieBs02.Count();

ieBs02 = ieBs02.Skip(1).Take(2);

//****此处会引发异常****
int count02 = ieBs02.Count();

误区:对 iq对象 和 ie对象 使用foreach时,对于循环的每项都要查询数据库

错误!

foreach针对的是数据集整体对象(迭代器?)。当使用foreach时,不管是iq对象还是ie对象,它们都是查询数据库一次,然后开始循环,直至循环结束。不过,当后续再次使用iq对象或ie对象的具体数据时,它们仍然会再次查询数据库。

注意:iq对象的结果是数据集。它只能把当前存储的表达式树转换为sql。它无法对其进行处理来做到一次一条的取出数据,因为根本就不可能!怎么能无中生有呢?

反向验证

也可以这样想:如果是一条一条取数据的话,程序怎么知道每次应该取哪条数据?

由上面的反例可以看出,一条一条查数据可以实现,但是太二逼。完全不如一次性全部读取数据的好。

其他

IQueryable和IEnumerable生成sql的测试代码

先说下结论:

NetFramework

测试环境:

            BaseSpDB bdb = new BaseSpDB();
            //不查询数据库
            IQueryable<BaseSupplier_OTAOnline> iqBs = bdb.baseSpByAll().Where(p => p.ID < 10);
            //不查询数据库
            IEnumerable<BaseSupplier_OTAOnline> ieBs = iqBs.AsEnumerable();
            //不查询数据库
            ieBs = ieBs.Where(p => p.ID > 5);

            //执行sql
            //只执行iq的条件
            //查询数据库
//exec sp_executesql N'SELECT [t0].[ID], [t0].[OTAName], [t0].[OnlineSupplier], [t0].[PushUrl], [t0].[Note], [t0].[EditCode], [t0].[AddUser], [t0].[PushDate], [t0].[GetDate], [t0].[EditDate], [t0].[AddDate]
//FROM [dbo].[BaseSupplier_OTAOnline] AS [t0]
//WHERE [t0].[ID] < @p0',N'@p0 int',@p0=10
            List<BaseSupplier_OTAOnline> bsList = ieBs.ToList();

            //再次查询数据库
//exec sp_executesql N'SELECT [t0].[ID], [t0].[OTAName], [t0].[OnlineSupplier], [t0].[PushUrl], [t0].[Note], [t0].[EditCode], [t0].[AddUser], [t0].[PushDate], [t0].[GetDate], [t0].[EditDate], [t0].[AddDate]
//FROM [dbo].[BaseSupplier_OTAOnline] AS [t0]
//WHERE [t0].[ID] < @p0',N'@p0 int',@p0=10
            foreach (var item in ieBs)
            {
                
            }

NetCore

测试环境:

            //不查询数据库
            IQueryable<BaseSupplier_OTAOnline> iqOta = ctx.BaseSupplier_OTAOnline.Where(p => p.ID < 10);

            //不查询数据库
            IEnumerable<BaseSupplier_OTAOnline> ieOta = iqOta.AsEnumerable();

            //不查询数据库
            ieOta = ieOta.Where(p => p.ID > 5);

            //执行sql
            //只执行iq的条件
            //查询数据库
//SELECT [p].[ID], [p].[AddDate], [p].[AddUser], [p].[EditCode], [p].[EditDate], [p].[GetDate], [p].[Note], [p].[OTAName], [p].[OnlineSupplier], [p].[PushDate], [p].[PushUrl]
//FROM [BaseSupplier_OTAOnline] AS [p]
//WHERE [p].[ID] < 10
            List<BaseSupplier_OTAOnline> bsList = ieOta.ToList();

            //再次查询数据库
//SELECT [p].[ID], [p].[AddDate], [p].[AddUser], [p].[EditCode], [p].[EditDate], [p].[GetDate], [p].[Note], [p].[OTAName], [p].[OnlineSupplier], [p].[PushDate], [p].[PushUrl]
//FROM [BaseSupplier_OTAOnline] AS [p]
//WHERE [p].[ID] < 10
            foreach (var item in ieOta)
            {

            }

参考

  1. LINQ查询中的IEnumerable<T>和IQueryable<T>
  2. LINQ使用细节之.AsEnumerable()和.ToList()的区别
  3. 建议29:区别LINQ查询中的IEnumerabl<T>和IQueryable<T> - 陆敏技《编写高质量代码改善C#程序的157个建议》
    2人点赞   日记本

 

 转 https://www.jianshu.com/p/daeae7ceec91?from=singlemessage&isappinstalled=0

标签:区别,IQueryable,sql,IEnumerable,t0,查询数据库,ID
来源: https://www.cnblogs.com/wl-blog/p/16140236.html