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類的實例,表示過期的絕對時間。

  protected void 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM