问题
随着您网站的成长,无论是普通的还是动态的内容,这两个因素导致网站载入变慢。许多用户造成了大量的web 服务器和数据库的请求。大量的数据需要强大的数据库处理能力来支持 它。为了防止花了很多钱,只需添加更多的Web服务器,更聪明的编程,以减少不必要的数据库或动态处理请求,可以明显增加你的web应用的整体速度
解决方案
使用OutputCacheAttribute去缓存那些不经常改变或只在具体action改变的数据。
讨论
在MVC3中缓存是非常容易的。简单的添加下边的attribute在一个controller的一个action上。
1
|
[OutputCache (Duration=600)] |
这将自动缓存视图的结果为600秒(或10分钟)并且共享每个用户访问此页。这意味着,如果你有1000名访问者访问在同一页上,可以减少成千上万的请求到数据库。通过简单的加载一个已经完全处理过的view,可以降低IIS的处理时间。输出缓存属性看起来很简单,但是当你开始寻找下边的引擎,它可是汽车盖子下复杂的引擎,除非你是一名机械师。(译者的意思是,你看起来很简单,但是内部实现很复杂).这个属性允许你定义了很多有关如何缓存,从期限到位置,甚至可以添加SQL依赖关系。这将在此配方探讨。
缓存的持续时间很简单:你告诉MVC视图应该缓存多少秒。位置是更复杂,这可能是客户端的浏览器,服务器,或它们的组合。对需要缓存的数据进行分析是确定在哪做缓存的好方法。如果它是个人数据,例如,一个定制的网页,缓存在本地用户的浏览器上是一件有意义的事。虽然缓存是伟大的,但也有局限性。通常情况下,主要的限制是内存,不是一切都可以缓存在服务器上。然而,最有趣的选项是SQL依赖关系。 OutputCache允许数据被缓存,直到它实际上是在数据库中的变化。这是一个非常有用的功能。例如,图书:不会每天都添加一本新书,所以这个持续时间可以被设置成24小时。然而,如果一本书在缓存过期之前就添加了或者1星期也没有新书呢?在第一种情况里,一个新的书不会以正确的方式出现,这可能会使用户不太高兴了。在第二个例子里,请求到服务器是不必要的,因为没有添加的新书。通过启用SQL依赖关系,如果book 的表变化了,缓存会尽快的重置。这完全是我们希望的效果。
提示:这是一个非常不错的功能,在其他编程语言时,你需要手动控制缓存。当数据变化时候你需要自己使缓存失效。在这一点上相信我,很容易你就落下了1-2个,忘记清空缓存。
在下边的例子,缓存将被设置到图书列表页。默认的,如果你不在VaryByPararm字段设置任何的值。MVC3 将自动的为每个可变组合创建一个独立的缓存。这是一个相当不错的功能。
然而,在这本书的例子中,一个关键字搜索字段是作为一个参数被接收的。因为输入的数以百计,而不是成千上万的不同的关键字组合,这个变量不应该被缓存(见上有关内存警告)。相反的params将被定义成排除这个变量。下面是一个更新的BooksController,使此页面上的缓存:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
using System; usingSystem.Collections.Generic; usingSystem.Data; usingSystem.Data.Entity; usingSystem.Linq; usingSystem.Linq.Dynamic; usingSystem.Web; usingSystem.Web.Mvc; usingMvcApplication.Models; usingMvcApplication.Utils; usingPagedList; namespaceMvcApplication.Controllers { public class BooksController : Controller { privateBookDBContextdb = new BookDBContext(); // // GET: /Books/ [OutputCache(Duration=600, VaryByParam="sortOrder;filter;page")] publicViewResult Index(string sortOrder, string filter, string Keyword, int page = 1) { ... return View(books.ToPagedList(currentPage, maxRecords)); } ... } } |
此代码将成为一个不错的缓存解决方案,并立即减少服务器的负载。这个例子将扩大到包括SQL依赖关系,因为它需要一些安装工作在开始使用它之前。首先,需要更新Web.config文件。首先,必须定义一个数据库连接;其次,缓存部分必须被定义为SQL依赖关系如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<? xml version = "1.0" ?> < configuration > < connectionStrings > < add name = "ApplicationServices" connectionString="data source=.SQLEXPRESS;Integrated Security = SSPI ;AttachDBFilename=|DataDirectory|aspnetdb.mdf; User Instance = true " providerName = "System.Data.SqlClient" /> < add name = "BooksDBContext" connectionString = "Server=.SQLEXPRESS;Database= MvcApplication4.Models.BookDBContext;Trusted_Connection=true" providerName = "System.Data.SqlClient" /> </ connectionStrings > ... < system.web > < caching > < sqlCacheDependency enabled = "true" pollTime = "2000" > < databases > < add name = "MvcApplication4.Models.BookDBContext" connectionStringName = "BooksDBContext" /> </ databases > </ sqlCacheDependency > </ caching > ... </ system.web > ... </ configuration > |
在上面的例子,pollTime变量设置为2000毫秒,这意味着每2秒,缓存数据库将查询改变。这根据您的需求而改变。现在的Global.asax.cs需要更新。在Application_Start函数, SQL缓存依赖项,必须建立,并且每个需要监听变化的表要以EnableTableForNotifications功能的SqlCacheDependencyAdmin类建立。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using MvcApplication.Models; using System.Data.Entity; using System.Globalization; using System.Threading; using MvcApplication.Utils; namespaceMvcApplication { public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { Database.SetInitializer< BookDBContext >(newBookInitializer()); AreaRegistration.RegisterAllAreas(); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); String connectionString =System.Configuration.ConfigurationManager.ConnectionStrings["BooksDBContext"].ConnectionString; System.Web.Caching.SqlCacheDependencyAdmin.EnableNotifications(connectionString); System.Web.Caching.SqlCacheDependencyAdmin.EnableTableForNotifications(connectionString, "books"); } } } |
接下来,是一个命令行提示符需要执行几个动作完成 SQL通知。在Windows中,执行以下步骤: 按开始 - >运行 键入cmd,然后按Enter键
cd %windir%Microsoft.NETFrameworkv4.0.30319
aspnet_regsql.exe -S .SQLEXPRESS -ed
-d MvcApplication4.Models.BookDBContext -et -t books –E
务必替换您的信息的服务器,数据库和表名。此外,如果您的数据库包含用户名和密码,你将需要添加额外的输入他们的参数(- U和- P)。一旦最后的命令运行时,两个成功的消息应显示:表明对数据库中的缓存的成功使,和第二次成功使在指定表上的缓存。最后,BooksController需要稍有变化,使SQL依赖。此外,由于应用程序将被通知的变化,前边设置很短的时间需要扩展到使用Int32值的最大值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Linq.Dynamic; using System.Web; using System.Web.Mvc; using MvcApplication.Models; using MvcApplication.Utils; using PagedList; namespaceMvcApplication.Controllers { public class BooksController : Controller { privateBookDBContextdb = new BookDBContext(); // // GET: /Books/ [OutputCache(Duration=Int32.MaxValue, SqlDependency ="MvcApplication4.Models.BookDBContext:books",VaryByParam="sortOrder,filter,page")] publicViewResult Index(string sortOrder, string filter, string Keyword, int page = 1) { ... return View(books.ToPagedList(currentPage,maxRecords)); } ... } } |
在以前的MVC版本中,部分缓存不支持,这意味着,只有一个整个action的结果可以被缓存。现在的MVC3支持部分缓存。为了实现这一点,你会像在配方1.14中那样创建一个child action,Ajax提交表单。在BookCommentsController两个action只返回一个PartialView, 这些child action都可以缓存无需缓存父action。这是另一个伟大的方式来隔离你的代码和缓存当某一部分不经常发生变化。