通过输出缓存提升性能 (C#)
本教程介绍如何通过利用输出缓存来显著提高 ASP.NET MVC Web 应用程序的性能。 你将了解如何缓存从控制器操作返回的结果,以便无需在每次新用户每次调用操作时创建相同的内容。
本教程的目的是说明如何通过利用输出缓存来显著提高 ASP.NET MVC 应用程序的性能。 使用输出缓存可以缓存控制器操作返回的内容。 这样,无需每次调用同一控制器操作时生成相同的内容。
例如,假设 ASP.NET MVC 应用程序在名为 Index 的视图中显示数据库记录列表。 通常,每次用户调用返回索引视图的控制器操作时,都必须通过执行数据库查询从数据库检索数据库记录集。
另一方面,如果利用输出缓存,则可以避免在每次任何用户调用同一控制器操作时执行数据库查询。 可以从缓存中检索视图,而不是从控制器操作重新生成。 通过缓存,可以避免在服务器上执行冗余工作。
启用输出缓存
可以通过向单个控制器操作或整个控制器类添加 [OutputCache] 属性来启用输出缓存。 例如,清单 1 中的控制器公开名为 Index () 的操作。 Index () 操作的输出缓存 10 秒。
列表 1 - Controllers\HomeController.cs
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
[HandleError]
public class HomeController : Controller
{
[OutputCache(Duration=10, VaryByParam="none")]
public ActionResult Index()
{
return View();
}
}
}
在 ASP.NET MVC 的 Beta 版本中,输出缓存不适用于类似 的 http://www.MySite.com/
URL。 相反,必须输入类似于 的 http://www.MySite.com/Home/Index
URL。
在清单 1 中,Index () 操作的输出缓存 10 秒。 如果需要,可以指定更长的缓存持续时间。 例如,如果要将控制器操作的输出缓存一天,则可以将缓存持续时间指定为 86400 秒 (60 秒 * 60 分钟 * 24 小时) 。
无法保证缓存内容的时间量与指定的时间一长。 当内存资源不足时,缓存将开始自动逐出内容。
清单 1 中的主控制器返回清单 2 中的“索引”视图。 这个观点没有什么特别之处。 “索引”视图仅显示当前时间, (请参阅图 1) 。
清单 2 - Views\Home\Index.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Index</title>
</head>
<body>
<div>
The current time is: <%= DateTime.Now.ToString("T") %>
</div>
</body>
</html>
图 1 - 缓存索引视图
如果在浏览器的地址栏中输入 URL /Home/Index 并重复点击浏览器中的“刷新/重新加载”按钮,多次调用 Index () 操作,则“索引”视图显示的时间在 10 秒内不会更改。 显示同一时间,因为视图已缓存。
请务必了解,为访问应用程序的每个人缓存相同的视图。 调用 Index () 操作的任何人都将获得相同缓存版本的“索引”视图。 这意味着 Web 服务器为索引视图提供服务所必须执行的工作量大大减少。
清单 2 中的视图恰好正在执行一些非常简单的事情。 视图仅显示当前时间。 但是,可以同样轻松地缓存显示一组数据库记录的视图。 在这种情况下,每次调用返回视图的控制器操作时,都不需要从数据库检索数据库记录集。 缓存可以减少 Web 服务器和数据库服务器必须执行的工作量。
请勿在 MVC 视图中使用页面 <%@ OutputCache %> 指令。 此指令正从Web Forms领域流传,不应在 ASP.NET MVC 应用程序中使用。
缓存内容的位置
默认情况下,使用 [OutputCache] 属性时,内容缓存在三个位置:Web 服务器、任何代理服务器和 Web 浏览器。 可以通过修改 [OutputCache] 属性的 Location 属性来准确控制内容的缓存位置。
可以将 Location 属性设置为以下任何一个值:
·任何
·客户
·下游
·服务器
· 无
·ServerAndClient
默认情况下,Location 属性的值为 Any。 但是,在某些情况下,你可能只想在浏览器或服务器上缓存。 例如,如果要缓存针对每个用户个性化的信息,则不应在服务器上缓存信息。 如果向不同的用户显示不同的信息,则应仅在客户端上缓存该信息。
例如,清单 3 中的控制器公开一个名为 GetName () 的操作,该操作返回当前用户名。 如果 Jack 登录到网站并调用 GetName () 操作,则该操作将返回字符串“Hi Jack”。 如果随后 Jill 登录到网站并调用 GetName () 操作,则她还将获取字符串“Hi Jack”。 在 Jack 最初调用控制器操作后,该字符串将缓存在 Web 服务器上供所有用户使用。
列表 3 - Controllers\BadUserController.cs
using System.Web.Mvc;
using System.Web.UI;
namespace MvcApplication1.Controllers
{
public class BadUserController : Controller
{
[OutputCache(Duration = 3600, VaryByParam = "none")]
public string GetName()
{
return "Hi " + User.Identity.Name;
}
}
}
最有可能的是,清单 3 中的控制器无法按所需方式工作。 你不希望向 Jill 显示消息“Hi Jack”。
切勿在服务器缓存中缓存个性化内容。 但是,你可能希望在浏览器缓存中缓存个性化内容以提高性能。 如果在浏览器中缓存内容,并且用户多次调用同一控制器操作,则可以从浏览器缓存而不是服务器检索内容。
清单 4 中修改的控制器缓存 GetName () 操作的输出。 但是,内容仅缓存在浏览器上,而不缓存在服务器上。 这样,当多个用户调用 GetName () 方法时,每个人都会获取自己的用户名,而不是其他人的用户名。
列表 4 - Controllers\UserController.cs
using System.Web.Mvc;
using System.Web.UI;
namespace MvcApplication1.Controllers
{
public class UserController : Controller
{
[OutputCache(Duration=3600, VaryByParam="none", Location=OutputCacheLocation.Client, NoStore=true)]
public string GetName()
{
return "Hi " + User.Identity.Name;
}
}
}
请注意,清单 4 中的 [OutputCache] 属性包括设置为 OutputCacheLocation.Client 值的 Location 属性。 [OutputCache] 属性还包括 NoStore 属性。 NoStore 属性用于通知代理服务器和浏览器,它们不应存储缓存内容的永久副本。
改变输出缓存
在某些情况下,可能需要不同缓存版本的相同内容。 例如,假设要创建母版/详细信息页。 母版页显示电影标题列表。 单击标题时,将获取所选电影的详细信息。
如果缓存详细信息页,则无论单击哪个电影,都会显示同一电影的详细信息。 第一个用户选择的第一部影片将显示给所有未来的用户。
可以通过利用 [OutputCache] 属性的 VaryByParam 属性来解决此问题。 通过此属性,可以在表单参数或查询字符串参数发生变化时创建相同内容的不同缓存版本。
例如,清单 5 中的控制器公开了名为 Master () 和 Details () 的两个操作。 Master () 操作返回电影标题列表,Details () 操作返回所选电影的详细信息。
列表 5 - Controllers\MoviesController.cs
using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class MoviesController : Controller
{
private MovieDataContext _dataContext;
public MoviesController()
{
_dataContext = new MovieDataContext();
}
[OutputCache(Duration=int.MaxValue, VaryByParam="none")]
public ActionResult Master()
{
ViewData.Model = (from m in _dataContext.Movies
select m).ToList();
return View();
}
[OutputCache(Duration = int.MaxValue, VaryByParam = "id")]
public ActionResult Details(int id)
{
ViewData.Model = _dataContext.Movies.SingleOrDefault(m => m.Id == id);
return View();
}
}
}
Master () 操作包括值为“none”的 VaryByParam 属性。 调用 Master () 操作时,将返回主视图的相同缓存版本。 任何窗体参数或查询字符串参数都将被忽略, (请参阅图 2) 。
图 2 - /Movies/Master 视图
图 3 - /Movies/Details 视图
Details () 操作包括一个值为“Id”的 VaryByParam 属性。 将 Id 参数的不同值传递给控制器操作时,将生成不同缓存版本的“详细信息”视图。
请务必了解,使用 VaryByParam 属性会导致更多的缓存,而不是更少。 为每个不同版本的 Id 参数创建不同的缓存版本的“详细信息”视图。
可以将 VaryByParam 属性设置为以下值:
* = 每当窗体或查询字符串参数发生变化时,创建不同的缓存版本。
none = 从不创建不同的缓存版本
参数的分号列表 = 每当列表中任何窗体或查询字符串参数发生变化时,创建不同的缓存版本
创建缓存配置文件
作为通过修改 [OutputCache] 属性来配置输出缓存属性的替代方法,可以在 Web 配置 (web.config) 文件中创建缓存配置文件。 在 Web 配置文件中创建缓存配置文件具有几个重要优势。
首先,通过在 Web 配置文件中配置输出缓存,可以控制控制器操作在一个中心位置缓存内容的方式。 可以创建一个缓存配置文件,并将配置文件应用于多个控制器或控制器操作。
其次,无需重新编译应用程序即可修改 Web 配置文件。 如果需要为已部署到生产环境的应用程序禁用缓存,只需修改 Web 配置文件中定义的缓存配置文件即可。 将自动检测并应用对 Web 配置文件所做的任何更改。
例如,清单 6 中的 <缓存> Web 配置部分定义了名为 Cache1Hour 的缓存配置文件。 缓存<>部分必须出现在 Web 配置文件的 system.web> 节中<。
清单 6 - 适用于 web.config的缓存部分
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Cache1Hour" duration="3600" varyByParam="none"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
清单 7 中的控制器演示了如何使用 [OutputCache] 属性将 Cache1Hour 配置文件应用于控制器操作。
列表 7 - Controllers\ProfileController.cs
using System;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class ProfileController : Controller
{
[OutputCache(CacheProfile="Cache1Hour")]
public string Index()
{
return DateTime.Now.ToString("T");
}
}
}
如果调用清单 7 中控制器公开的 Index () 操作,则相同的时间将返回 1 小时。
总结
输出缓存提供了一种非常简单的方法,可显著提高 ASP.NET MVC 应用程序的性能。 本教程介绍了如何使用 [OutputCache] 属性来缓存控制器操作的输出。 你还了解了如何修改 [OutputCache] 属性(如 Duration 和 VaryByParam 属性)的属性,以修改内容的缓存方式。 最后,你了解了如何在 Web 配置文件中定义缓存配置文件。