ASP.NET MVC 缓存控制

缓存(cache)

1.缓存是一种以空间换时间的技术, 比如, cpU的二级缓存,Windows的文件缓存。

2.减少服务器的负荷,默认存放在内存里面,不过是可以修改的。

3.缓存存在失效的情况。Asp.net 缓存主要分为页面缓存,数据源缓存,数据缓存。

4. 页面缓存: %@OutPutCache Duration="15" VaryByParam="none" %

 

属性说明

VaryByParam是指页面根据使用 POST  GET 发送的名称/值对(参数)来更新缓存的内容,多个参数用分号隔开。如果不希望根据任何参数来改变缓存内容,请将值设置为 none。如果希望通过所有的参数值改变都更新缓存,请将属性设置为星号 (*) 

 

      例如: http://localhost:1165/16-4-3/WebForm1.aspx?p=1 
  则可以在WebForm1.aspx页面头部声明缓存:<%@ OutputCache Duration="60" VaryByParam="p" %> 

  以上代码设置页面缓存时间是60秒,并根据p参数的值来更新缓存,即p的值发生变化才更新缓存。 

  如果一直是WebForm1.aspx?p=1访问该页,则页面会缓存当前数据,当p=2时又会执行后台代码更新缓存内容。

  如果有多个参数时,如:http://localhost:1165/16-4-3/WebForm1.aspx?p=1&n=1

  可以这样声明:<%@ OutputCache Duration="60" VaryByParam="p;n" %>

 

CacheProfile用于调用Web.config配置文件中设置的缓存时间。这是可选属性,默认值为空字符("")

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplicationCacheSample.Models;
using System.Web.UI;

namespace MvcApplicationCacheSample.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/
        [OutputCache(CacheProfile="employee")]
        public ActionResult Index()
        {

            Response.Cache.SetOmitVaryStar(true);

            ViewBag.CurrentTime = DateTime.Now.ToString();


            //这里目前作为演示,是直接硬编码,实际上可能是读取数据库的数据
            var employees = new[]{
                new Employee(){ID=1,Name="ares",Gender="Male"}
            };
            return View(employees);
        }

    }
}

  

例如在Web.config中加入配置:

<system.web>
<caching>
  <outputCacheSettings>
    <outputCacheProfiles>
      <add name="employee" duration="10" enabled="true" location="ServerAndClient" varyByParam="none"/>
    </outputCacheProfiles>
  </outputCacheSettings>
</caching>
</system.web>

  页面中声明:

<%@ OutputCache CacheProfile="CacheTest" VaryByParam="none"%>

  注意:包含在用户控件(.ascx 文件)中的 @ OutputCache 指令不支持此属性。在页中指定此属性时,属性值必须与 outputCacheSettings 节下面的 outputCacheProfiles 元素中的一个可用项的名称匹配。如果此名称与配置文件项不匹配,将引发异常。

  如果每个页面的缓存时间相同,则不需要每个页面设置,而是通过统一一个地方控制,这样就可以更好的统一控制所有页面的缓存时间。如果想改变缓存时间,只需要改一下web.config的配置信息即可,而不用每个页面去修改。

 

VaryByControl通过用户控件文件中包含的服务器控件来改变缓存(值是控件ID,多控件用分号隔开)。

 

 ASP.NET 页和用户控件上使用 @ OutputCache 指令时,需要该属性或 VaryByParam 属性。

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm2.aspx.cs" Inherits="CacheWebApp._16_4_3.WebForm2" %>
<%@ OutputCache Duration="60" VaryByParam="none" VaryByControl="DropDownList1" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>根据控件页面缓存</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
         <%=DateTime.Now %>       
        <br>
    <asp:DropDownList ID="DropDownList1" runat="server">
        <asp:ListItem>beijing</asp:ListItem>
        <asp:ListItem>shanghai</asp:ListItem>
        <asp:ListItem>guangzhou</asp:ListItem>
        </asp:DropDownList>
        <asp:Button ID="Button1" runat="server" Text="提交" />
    </div>
    </form>
</body>
</html>

以上代码设置缓存有效期是60秒,并且页面不随任何GETPOST参数改变(即使不使用VaryByParam属性,但是仍然需要在@ OutputControl指令中显式声明该属性)。如果用户控件中包含ID属性为“DropDownList1”的服务器控件(例如下拉框控件),那么缓存将根据该控件的变化来更新页面数据。

 

VaryByHeader可以根据用户请求中所提供的一些Header信息不同而决定是否读取缓存。我们可以看到在每个请求中都会包含一些Header信息

 

例如根据不同的语言,我们显然是有不同的版本的。或者根据用户浏览器不同,也可以缓存不同的版本。可以通过这样设置

VaryByHeader=”Accept-Language,User-Agent”

VaryByContentEncoding:一般设置为Accept-Encoding里面可能的Encoding名称,从上图也可以看出,Request里面是包含这个标头的。

VaryByCustom:则是一个完全可以定制的设置,例如我们可能需要根据用户角色来决定不同的缓存版本,或者根据浏览器的一些小版本号来区分不同的缓存版本,我们可以这样设置:VaryByCustom=”Role,BrowserVersion”,这些名称是你自己定义的,光这样写当然是没有用的,我们还需要在Global.asax文件中,添加一个特殊的方法,来针对这种特殊的需求进行处理。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Security;

namespace MvcApplicationCacheSample
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }

        public override string GetVaryByCustomString(HttpContext context, string custom)
        {
            switch(custom)
            {
                case "Role":
                    {
                        return string.Join(",", Roles.GetRolesForUser());
                    }
                case "BrowserVersion":
                    {
                        return context.Request.Browser.Type;
                    }

                default:
                    break;
            }

            return string.Empty;
        }
    }
}

  

 

 

 

页面输出缓存API

   Response类的Cache属性用于获取页面缓存策略。该方式的核心是调用System.Web.HttpCachePolicy。该类主要包含用于设置缓存特定的HTTP标头的方法和用于控制ASP.NET页面输出缓存的方法。与.NET Framework 1.x中的HttpCachePolicy类相比,.NET Framework 2.0中的HttpCachePolicy类得到了扩充和发展。主要是增加了一些重要方法,例如,SetOmitVarStar方法等。由于HttpCachePolicy类方法众多,下面简要说明几个常用方法。

  SetExpires方法:用于设置缓存过期的绝对时间。它的参数是一个DataTime类的实例,表示过期的绝对时间。

 protectedvoid Page_Load(object sender, EventArgs e)
{
// 通过API设置缓存
//相当于@OutputCache指令中的Duration属性
   Response.Cache.SetExpires(DateTime.Now.AddSeconds(10));
   Response.Cache.SetExpires(DateTime.Parse(
"6:00:00PM"));
}

  如上代码,第一行代码表示输出缓存时间是60秒,并且页面不随任何GETPOST参数改变,等同于“<%@ OutputCache Duration="60" VaryByParam="none" %>”。第二行代码设置缓存过期的绝对时间是当日下午6时整。

  SetLastModified方法:用于设置页面的Last-Modified HTTP标头。Last-Modified HTTP标头表示页面上次修改时间,缓存将依靠它来进行计时。如果违反了缓存限制层次结构,此方法将失败。该方法的参数是一个DataTime类的实例。

  SetSlidingExpiration方法:该方法将缓存过期从绝对时间设置为可调时间。其参数是一个布尔值。当参数为true时,Cache-Control HTTP标头将随每个响应而更新。此过期模式与相对于当前时间将过期标头添加到所有输出集的IIS配置选项相同。当参数为False时,将保留该设置,且任何启用可调整过期的尝试都将静态失败。此方法不直接映射到HTTP标头。它由后续模块或辅助请求来设置源服务器缓存策略。

  SetOmitVaryStar方法:ASP.NET 2.0新增的方法。用于指定在按参数进行区分时,响应是否应该包含vary:*标头。方法参数是一个布尔值,若要指示HttpCachePolicy不对其VaryByHeaders属性使用*值,则为true;否则为false

  SetCacheability方法:用于设置页面的Cache-Control HTTP标头。该标头用于控制在网络上缓存文档的方式。该方法有两种重载方式,所不同的是参数。一种重载方法的参数是HttpCacheability枚举值,包括NoCachePrivatePublicServerServerAndNoCacheServerAndPrivate(有关这些枚举值的定义,可参考MSDN)。另一种方法的参数有两个,一个参数是HttpCacheability枚举值,另一个参数是字符串,表示添加到标头的缓存控制扩展。需要注意的是,仅当与PrivateNoCache指令一起使用时,字段扩展名才有效。如果组合不兼容的指令和扩展,则此方法将引发无效参数异常。

 

 

 

1。整页缓存

[OutputCache(Duration = 3600, Location = OutputCacheLocation.Server, VaryByParam = "none")]
public ActionResult Index()
{
      //Do something.
}

  此外还可以在View窗口声明

<%@ OutputCache Duration="#ofseconds"
   Location="Any | Client | Downstream | Server | None | 
     ServerAndClient "
   Shared="True | False"
   VaryByControl="controlname"
   VaryByCustom="browser | customstring"
   VaryByHeader="headers"
   VaryByParam="parametername"
   VaryByContentEncoding="encodings"
   CacheProfile="cache profile name | ''"
   NoStore="true | false"
   SqlDependency="database/table name pair | CommandNotification"
%>

2 数据源缓存

<asp:ObjectDataSource EnableCaching="true" CacheDuration="20" ID="ObjectDataSource1" runat="server" SelectMethod="GetList" TypeName="BLL.Classes"></asp:ObjectDataSource>

3:自定义缓存

List<MODEL.Classes> list=null;

            if (Cache["myDog"] == null)
            {
                Cache["myDog"] = new Dog() { StrName = "小瑞瑞", StrType = "柯基犬" };
                Response.Write("保存了一只狗狗");
                //查询数据库 存入缓存
                list = new BLL.Classes().GetList();
                Cache["list"] =  list;
            }
            else
            {
                Dog myDog = Cache["myDog"] as Dog;
                Response.Write("myDog="+myDog.StrName);
                //从缓存里获取数据
                list = Cache["list"] as List<MODEL.Classes>;
            }
           myDog, list 就可以使用了

4.文件依赖项自定义缓存(新建一个文件 这里是txt)

List<MODEL.Classes> list=null;

            if (Cache["list"] == null)
            {
                //查询数据库 存入缓存
                list = new BLL.Classes().GetList();
                //设置绝对过期时间
                //Cache.Insert("list", list, null, DateTime.Now.AddSeconds(20),System.Web.Caching.Cache.NoSlidingExpiration);
                //设置滑动过期时间
                //Cache.Insert("list", list, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(10000));
                //带 文件缓存依赖
                string strPath = Server.MapPath("1.txt");
                //文件缓存依赖项
                System.Web.Caching.CacheDependency fileDep = new System.Web.Caching.CacheDependency(strPath);
                //创建带文件依赖的缓存 键值项
                Cache.Insert("list", list, fileDep, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration);

                Response.Write("依赖文件被修改了,又重新读取数据列表存入缓存中了~~!");
                
            }
            else
            {
                //从缓存里获取数据
                list = Cache["list"] as List<MODEL.Classes>;
            }
            使用缓存list

  

 

 

 一、MVC缓存问题:

在 ASP.NET MVC 3 中如果使用了 OutputCache,一定要在 Action 中添加下面的代码,切记!

Response.Cache.SetOmitVaryStar(true);

这是一个伴随ASP.NET从1.0到4.0的OutputCache Bug,ASP.NET MVC 3 是基于 ASP.NET 4.0 的,所以也躲不过。

 

问题演示

下面先来体验一下不加 Response.Cache.SetOmitVaryStar(true); 的情况。

示例Action代码:

[OutputCache(Duration = 120)]
public ActionResult SiteHome(int? pageIndex)
{
    ...
}

注:OutputCache.Location的默认值是OutputCacheLocation.Any(服务端、客户端、代理服务器端等都进行缓存)

第一次请求:

第二次请求(F5刷新浏览器):

第三次请求(F5刷新浏览器):

接着第四次请求会返回304,第五次请求又返回200。。。

 

再体验一下加 Response.Cache.SetOmitVaryStar(true); 的情况。

[OutputCache(Duration = 120)]
public ActionResult SiteHome(int? pageIndex)
{
    Response.Cache.SetOmitVaryStar(true);
    ...
}

第一次请求:

第二次请求(F5刷新浏览器):

第三次请求(F5刷新浏览器):

注:只要在缓存有效期内,服务器一直返回304。

 

问题分析

1. 200与304的区别

当返回状态码是200时,服务器端会将当前请求的整个页面全部发送给客户端(消耗下行带宽)。

当返回状态码是304时,由于客户端浏览器提供的 Last-Modified 时间在服务器端的缓存有效期内,服务器端只发送这个状态码,不发送页面的任何内容(几乎不消耗下行带宽),浏览器直接从本地缓存中获取内容。

所以,304的好处就是节约带宽,响应速度更快。

2. 对服务端缓存的影响

加不加 Response.Cache.SetOmitVaryStar(true),服务端的缓存情况都是一样的。只是不加 SetOmitVaryStar(true) 时,对于同一个客户端浏览器,每隔一次请求,服务器端就不管客户端浏览器的缓存,重新发送页面内容,但是只要在缓存有效期内,内容还是从服务器端缓存中读取。

 

问题危害

ASP.NET 缓存的这个诡异行为,让你在不知不觉中浪费了带宽资源。

 

感想

用 ASP.NET 开发多年,这个伴随 ASP.NET 从 1.0 到 4.0 的 OutputCache Bug 自己竟然在去年才发现。之前测试时第一次请求后按F5看返回304就以为没问题,而问题恰恰就在下一下F5,偶尔多按一下F5出现200也没特别留意。由此可见,细心对程序员来说是多么重要,很多bug、很多性能问题往往不是水平不够,而是不够细心。

优秀的程序员都是细心的人,不仅在写代码的时候细心,在生活中也同样细心。别看他木讷的样子,你对他所做的一切,他都会细心地观察到、体会到。做细心的程序员,珍惜细心的程序员!

 

二、ASP.NET MVC Action Filter - 缓存与压缩

缓存在开发高扩充性WEB程序的时候扮演着很重要的角色.我们可以将HTTP请求在一个定义的时间内缓存在用户的浏览器中,如果用户在定义的时间内请求同一个URL,那么用户的请求将会从用户浏览器的缓存中加载,而不是从服务器.你可以在ASP.NET MVC应用程序中使用下面的Action Filter来实现同样的事情:

using System;
using System.Web;
using System.Web.Mvc;

public class CacheFilterAttribute : ActionFilterAttribute
{
    /// <summary>
    /// Gets or sets the cache duration in seconds. The default is 10 seconds.
    /// </summary>
    /// <value>The cache duration in seconds.</value>
    public int Duration
    {
        get;
        set;
    }

    public CacheFilterAttribute()
    {
        Duration = 10;
    }

    public override void OnActionExecuted(FilterExecutedContext filterContext)
    {
        if (Duration <= 0) return;

        HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
        TimeSpan cacheDuration = TimeSpan.FromSeconds(Duration);

        cache.SetCacheability(HttpCacheability.Public);
        cache.SetExpires(DateTime.Now.Add(cacheDuration));
        cache.SetMaxAge(cacheDuration);
        cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
    }
}

你可以好像下面一样在你的Controller Action 方法中使用这个Filter :


[CacheFilter(Duration = 60)]
public void Category(string name, int? page)

下面是在firebug中当 缓存Filter 没有应用的时候的截图 :

NoCache

下面的截图是应用了 Cache Filter 时候的截图 :

Cache

 

另外一个很重要的事情就是压缩.现在的浏览器都可以接收压缩后的内容,这可以节省大量的带宽.你可以在你的ASP.NET MVC 程序中应用下面的Action Filter 来压缩你的Response :


using System.Web;
using System.Web.Mvc;

public class CompressFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(FilterExecutingContext filterContext)
    {
        HttpRequestBase request = filterContext.HttpContext.Request;

        string acceptEncoding = request.Headers["Accept-Encoding"];

        if (string.IsNullOrEmpty(acceptEncoding)) return;

        acceptEncoding = acceptEncoding.ToUpperInvariant();

        HttpResponseBase response = filterContext.HttpContext.Response;

        if (acceptEncoding.Contains("GZIP"))
        {
            response.AppendHeader("Content-encoding", "gzip");
            response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
        }
        else if (acceptEncoding.Contains("DEFLATE"))
        {
            response.AppendHeader("Content-encoding", "deflate");
            response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
        }
    }
}

然后将这个Filter应用到你的Controller Action 中 :

[CompressFilter]
public void Category(string name, int? page)

下面是没有应用压缩的时候的截图 :

Uncompressed

下面的截图是应用了压缩Filter后的情形 :

Compressed

你当然也可以将这两个Filter都应用到同一个Action方法上,就好像下面所示 :


[CompressFilter(Order = 1)]
[CacheFilter(Duration = 60, Order = 2)]
public void Category(string name, int? page)

下面是截图 :

Both

Enjoy!!!

下载源码Source.zip

posted @ 2012-11-18 22:39  AIの海雅  阅读(3325)  评论(1编辑  收藏  举报