LINQ中IQueryable 和IEnumberable的区别?用错问题很大

为啥要对比IQueryable和IEnumberable呢?有一次项目已经发布到生产环境,但是其中一个列表页面特别的慢,明明在测试环境正常,到线上怎么慢了呢?于是就检查这个页面的代码,表面看linq的写法也没有错,仔细观察才发现,在实现查询的时候仓储层返回的是IEnumberable类,而分页的条件写在了应用层。代码如下:.

//仓储的方法public IEnumerable<Team> GetTeams(){      return teamContext.Teams;}//应用层 条用仓储方法public IEnumerable<Team> GetTeams(int pageSize, int pageIndex, string keyName){   var rel = teamRepository.GetTeams()        .Where(w => w.Name.Contains(keyName))        .Skip(pageSize * (pageIndex - 1)).Take(pageSize).ToList();    return rel;}

这样写有啥问题呢?这就是我们今天要讨论的问题。

解惑

   为了查看本质,我们调试一下,并用SQL Server Profiler工具查看执行的SQL语句:

--详细字段省略SELECT [t].[Id], [t].[Name],[t].[age] FROM [Teams] AS [t]

这不是查询整个表的数据吗?不慢才怪,测试环境数据量小所以看不出来。

是用了IEnumberable的原因吗?我们把仓储层的IEnumberable换成IQueryable试一试。再调试一波,执行语句结果如下图:

LINQ中IQueryable 和IEnumberable的区别?用错问题很大

从上图可以看出,SQL调用了带分页参数的字符串SQL语句。这样一执行快多了,几乎秒级。

从上可以看出,在对数据库的操作中IQueryable比IEnumberable快,本质上是标注了IQueryable类型的linq语句才会翻译成SQL,而IEnumberable只是在内存中执行linq指令。

结语

    IQueryable和IEnumberable的本质区别是:IQueryable属于Linq to Sql,它在执行时会转换成SQL传递到数据库。IEnumberable属于Linq to Object,IEnumberable类型是在内存中执行各种linq指令,本案例中的分页就是把全部数据读取出来,然后在内存中执行分页操作,所以遇到大量数据会卡。IQueryable继承自IEnumerable,他们可以通过AsEnumerable()和AsQueryable()相互转换。

    在使用linq中,必须正确的去使用,EF不是性能不高,从某种意义上来看没有正确的去使用。这个案例是一个刚刚来公司的程序员未调用现成的分页方法而是自己去写,这种尝试的精神可以肯定,但是在真的开发环境中还是调用成熟的解决方案,以免造成生产环境级别的错误。

    希望本文对你有收获,同时你对IQueryable和IEnumberable的区别有啥看法,欢迎留言讨论。