在谈谈EF的性能优化之前请允许笔者废话几句。虽然说笔者以前有接触EF,但真正意义上的接触EF算是今年的8月份吧!那时公司里面有一个产品模块化的项目需要用到ORM。当时有两个选择1.EF,2.NHibernate。 说实在的两者的口碑都不怎么样...最后我还是支持了下微软的EF,毕竟做.Net开发用自家的会方便很多把。一开始在网上查得的一些博文会说EF各种性能不好!(PS:今天跟客户谈项目的时候又被喷了)不过我还是傻里傻气的保持乐观的态度,性能不好就想办法让它好起来!!!不做怎么知道呢!下面我一些优化的方案一一列举出来(目前只总结了查询),写的不准确的地方,还希望大家能够指出来。
说到EF性能优化不得不说一个工具MiniProfiler,(不过也可以直接用Sqlserver profiler)MiniProfiler是StackOverFlow团队设计的一款对.net的性能分析小程序。在这里我们可以使用MiniProfiler嵌入页面查看页面处理的周期和Sql语句执行的周期及Sql语句。
可以通过以下步骤来使用它:(环境.NET4.0 + EntityFramewrok4.4)
1.通过Nuget下载MiniProfiler和MiniProfiler.EF (MiniProfiler.EF5兼容EntityFramework5.0以下的版本)
2.修改Global.asax文件,添加如下代码
protected void Application_Start() { //SQL跟踪 StackExchange.Profiling.MiniProfilerEF.Initialize(); } protected void Application_BeginRequest() { MiniProfiler.Start(); } protected void Application_EndRequest() { MiniProfiler.Stop(); }
3.在需要调试的页面中使用它,需要在_Layout.cshtml添加MiniProfiler的引用(我这里使用的是ASP.NET MVC)
//引用 @using StackExchange.Profiling //Body @MiniProfiler.RenderIncludes()
若是在页面中出现如下画面就表示MiniProfiler已经配置成功了,可以进入正式进入优化的环节了!!!
具体解决问题我总结了下有几个方案:
1.使用EntityFramework5及以上的版本代替EF4.0及以下的版本(使用.Net4.0的可以略过)
2.禁用延迟加载
默认情况下,延迟加载被支持,如果你希望禁用它,必须显式声明,最好的位置是在 DbContext 的构造器中。
public MyContext() { this.Configuration.LazyLoadingEnabled = false; }
因为若使用延迟加载,每次我们需要访问属性的时候都会访问数据这样累加起来的开销是很大的。
3.使用贪婪加载(又叫预加载就是数据库的多表查询)
这点其实也跟上面的一样响应了一个原则尽量的减少数据库的访问次数,
articles = dbContext.Articles.Include("Category");
4.尽量在sql语句拼凑好的时候进行ToList()或者遍历IQueryable<T>,因为一旦调用IQueryable<T>就相当于进行的数据库的查询
错误写法:
dbContext.Articles.ToList().Where(u=>u.id==1);
正确写法:
dbContext.Articles.Where(u=>u.id==1).ToList();
这些写法的意思就是把数据拼凑好,再访问数据库。不然就成了从数据库获取全部数据就再过滤,假如数据库有几万甚至几十万数据。那就不得了了!
这里再提供一个筛选+分页的写法(很好用)
var model = dbContext.MyModules.OrderByDescending(u => u.CreateTime).Skip((pageInfo.PageIndex - 1) * pageInfo.PageSize).Take(pageInfo.PageSize).Select(u => new { u.ID, u.Name, u.Version, u.TagList, u.UserID, u.DownLoadTimes, u.CreateTime }).ToList();
5.缓存数据
尽量把频繁用到的数据一次性加载到内存当中,适应一些频繁调用数据库进行计算的功能(好像跟第4调有些冲突,但是又的时候减少数据库访问次数反而更好。具体问题具体分析了)
我项目中有个导航菜单就是频繁的访问数据库导致性能低下(一开始得到1级菜单,然后通过1级获取2级菜单,2级获取3级....)
解决方法就是一次性把所有数据载入内存,然后通过内存进行检索,时间马上就降了3s左右。相对这种情况其实还是比较多的!
说了这么多,总结一下就两点:
1.保证拼凑的sql语句性能要高
2.减少数据库的访问次数
可能大牛觉得这估计都是废话!不过这些方法还是挺适合中下阶级的。
不足的以后再补充~!