EF性能讨论及EF查询中的“坑”

作者:陆金龙    发表时间:2014-12-07 13:33   


引子

曾经与一位同事讨论EntityFramework(以下简称EF),同事很不看好EF的使用,说会有执行效率问题。网上也有很多朋友讨论说数据表的记录达到几十万、上百万级别的时候,会有严重的性能问题。本人恰好在一个MVC+EF的项目中使用EF对六七百万记录的表进行查询的经历,经过对比发现EF查询与ADO.Net相比并没有明显的性能差别,不过使用上有一些“坑”,不留神的话难免会掉进里面。

EF的确有性能损失

与ado.net相比,使用EF时在代码层面多了一步,生成sql脚本,会多消耗一点CPU。在数据库层面是一样的,都是执行sql脚本。

EF性能损失可忽略

由于EF执行消耗是代码级别的,远远小于数据库查询本身消耗的时间(相差几个数量级),在整个系统级(服务器网络带宽  处理请求  各层  数据库),EF这个环节的性能消耗的影响微乎其微。经过测试,它接近ado.net的访问,可以应用到互联网。

有些特殊的情况下,EF生成的sql语句不好,会对查询效率有一些影响。这种情况可以考虑自己写sql脚本或写存储过程,使用Ado.Net进行查询。

EF查询中的“坑”

 先看下面这个查询方法:

 public IQueryable<T> LoadEntities(Func<T, bool> whereLambda)

 {
     return db.Set<T>().Where<T>(whereLambda).AsQueryable();
 }

上述查询方式,EF是直接从表中取出所有的数据到内存中,然后再做一次检索。在数据量小的时候并不太影响性能。但是在数据量稍大时,这种方式就会给服务器造成很大的负担,并导致处理超时。几十万上百万记录的数据表的查询当然就行不通了。网上关于EF处理不了大数据量的查询,估计有一部分是上述这种情况。

System.Linq.Expressions命名空间的Expression可以帮我们解决以上问题。Expression<TDelegate>类可以以表达式目录树的形式将强类型 lambda 表达式表示为数据结构,从而在编译阶段产生我们想要的SQL代码。可将上述方法的参数做如下修改:

 public IQueryable<T> LoadEntities(Expression<Func<T, bool>> whereLambda)
  {
        return db.Set<T>().Where<T>(whereLambda).AsQueryable();
  }

经过上述修改后,查询效果基本接近Ado.net了。笔者对两个真实的项目进行了比较,一个是WebForm+Ado.Net,一个是MVC+EF,业务数据表是记录数在600万以上,查询效率没有明显的差异。至于更大数据量的没有测试,有兴趣的朋友可以一试。

(转载本文请注明作者和原文链接 ,请勿用于任何商业用途)