建立mvc web application,采用code first 的方式,MVC5,EF6.0 整了一个网站。开发完以后。直接publish。就这样部署到服务器上了。web
在使用过程当中发现,网站打开的速度有点慢。并且每隔一段时间不使用,网站的打开速度就变慢。sql
问题分析:数据库
一开始首先想到的是IIS的应用程序池释放的问题。浏览器
后来配置了iis仍是过一段时间访问变慢。bash
后来为了快速解决这个问题,只能先作了一个bat文件,在服务器端模拟一段时间内访问这个网站一下。这样用户在访问的时候不会体会到网站的访问速度变慢的问题。服务器
@echo 正在关掉全部的IE进程(须要设置默认浏览器是IE)
taskkill /im iexplore.exe /f /t
@echo 正在经过ping来延迟80秒钟,以方便IE打开页面
ping 127.0.0.1 -n 10
@echo 正在访问 http://localhost
start "C:\Program Files\Internet Explorer\iexplore.exe" http://localhost
这样隔一段时间变慢的问题已经解决了,可是尚未查找到这是什么缘由。架构
后来看到一篇文章说 是由于EF 的缘由。mvc
EF方面的缘由:app
一、Code First第一次启动会对比程序中的Model与数据库表(database initializer ),生成Model与数据库的映射视图
二、随着EF的开源,EF从6开始就不会包含在.net Framework中,安装.net Framework默认是不会安装EF的。所以EF程序集就没有生成本地镜像,这样每次程序启动,EF的代码都会经过just-in-time (JIT) compiler(即时编译器)把MSIL中间代码编译成本机能识别的本地代码。由于这个生成的本地代码存在程序运行的进程里面的内存中,它将回收当程序进程被终止(例如:iis程序池回收,程序池默认是按需触发运行的,没人访问它就不启动了)。因为EF框架仍是比较大的,EF6文件大小到4-5M了,因此每次启动都要重写编译本地代码有比较明显的性能影响。
第2、优化方案框架
我主要是经过如下几方面来优化
1、安装Application Initialization

若是仅配置程序池StartMode为AlwaysRunning还不放心的话, 也能够同时针对站点开启preload和DoAppInitAfterRestart。
设置应用程序池以下图:

设置网站以下图

配置好后,测试了下,效果十分不错。 回收程序池后首次打开各站点,延迟都很低。 其实这个模块的思路和定时从外部触发一个访问是同样的,只是,更好的地方在于,它自己在程序池回收重启的时候就完成了这件事,而不会让外部访问有机会遇到首次访问的状况。
2、用Ngen安装生成EF的本地镜像
一、打开cmd窗口
二、定位到dll所在的目录,如:cd d:\website1\bin,切换到程序的bin目录。
三、运行ngen命令
For 32 bit run:
%WINDIR%\Microsoft.NET\Framework\v4.0.30319\ngen install EntityFramework.SqlServer.dll
For 64 bit run: %WINDIR%\Microsoft.NET\Framework64\v4.0.30319\ngen install EntityFramework.SqlServer.dll
注意:这里根据你本身机器(是32仍是64)和.net版本,选择相应的命令,只须要安装EntityFramework.SqlServer.dll,由于安依赖EntityFramework.dll,会自动安装生成EntityFramework.dll的本地镜像。
3、禁用第一次ef查询对表__MigrationHistory的问题
使用了ef的Code first会在第一次ef查询的时候会对__MigrationHistory访问,是为了检查数据库和model是否匹配,以保证ef能正常运行。经过监测会先执行下面的sql:
- SELECT
- [GroupBy1].[A1] AS [C1]
- FROM ( SELECT
- COUNT(1) AS [A1]
- FROM [dbo].[__MigrationHistory] AS [Extent1]
- ) AS [GroupBy1]
- GO
- SELECT TOP (1)
- [Extent1].[Id] AS [Id],
- [Extent1].[ModelHash] AS [ModelHash]
- FROM [dbo].[EdmMetadata] AS [Extent1]
- ORDER BY [Extent1].[Id] DESC
- GO
这段sql语句其实中只是在开发的时候有用,发布到生产环境,能够把这个给禁用了以提升性能。解决办法:
Application_Start加代码
- Database.SetInitializer<lanhuBlog.DAL.BlogContext>(null);
lanhuBlog.DAL.BlogContext这是我项目的EF上下方类,你要根据你的项目替换成本身的EF上下方类。
4、Model和DAl单独的分层的
用vs建一个mvc项目,Model、DAL、Controller、View都在Web项目里面。为了减小model和DAL致使从新编译dll带来的性能影响。我把Model和DAL都单独的分层,编译成单独的dll了。
5、EF Pre-Generated Mapping Views(预生成映射视图)
Application_Start加入下面代码:
- using (var dbcontext = new EFDbContext())
- {
- var objectContext = ((IObjectContextAdapter)dbcontext).ObjectContext;
- var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
- mappingCollection.GenerateViews(new List<EdmSchemaError>());
- //对程序中定义的全部DbContext逐一进行这个操做
- }
6、补充
若是你以为这尚未解决”过了一段时间不访问页面而后再次打开页面变慢“的问题,并且不能忍受第一次访问仍是有点慢,能够设置应用程序池的”闲时超时“和回收”固定时间间隔“长一些或者建一个计划任务定时去访问使用了ef的页面,这样给ef热身,让ef不变冷,这样能够防止长时间不请求网站,应用程序进程中止再次访问变慢的问题。设置应用程序池的时间以下图:

闲时超时默认是20分钟,若是在超过20分钟都没有请求这个应用程序池工做进程就要关闭。这里你能够设置根据本身须要设置长一些。