MVC中配置OutputCache的VaryByParam參數無效的問題


在項目使用OutputCacheAttribute是遇到了問題,當我想在配置文件web.config中配置OutputCache的VaryByParam時竟然不起作用,下面是相關代碼:
文件FaceController.cs

    [OutputCache(CacheProfile = "faceProfile")] 
    public ActionResult Index() 
    { 
        return View(); 
    } 

文件index.cshtml

<h2>@DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")</h2> 

web.config的cache配置

  <system.web> 
    <caching> 
      <outputCacheSettings> 
        <outputCacheProfiles> 
          <add  name="faceProfile" duration ="180"  varyByParam="none" enabled="true"/> 
        </outputCacheProfiles> 
      </outputCacheSettings> 
    </caching> 
<system.web> 

下面是我的測試:
請求 /face/index 頁面,輸出: 2014-08-02 18:40:56.184
再次請求 /face/index 頁面,輸出: 2014-08-02 18:40:56.184 (不變)
因為我指定了varyByParam="none",所以我添加參數或改變參數,輸出的時間應該不變才對,可是:
請求 /face/index/?p=19999 頁面,輸出: 2014-08-02 18:42:29.720 (變了)
請求 /face/index/?p=10000 頁面,輸出: 2014-08-02 18:43:30.981 (變了)
請求 /face/index/?abcd 頁面,輸出: 2014-08-02 18:44:00.440 (變了)
請求結果隨着參數的變化而變化,所以它應該是為每個參數都緩存了一個版本相當於設置了varyByParam="*"
從測試結果可以看出配置文件中設置的duration ="180"有起到作用,而varyByParam="none"沒有起到作用.
然后我試着把varyByParam="none"直接寫到代碼里:

    [OutputCache( Duration=180,VaryByParam="none")] 
    public ActionResult Index() 
    { 
        return View(); 
    } 

這次結果它正確進行緩存頁面,沒有為每個參數緩存一個版本,這使我很困惑,然后我看了一下OutputCacheAttribute的源碼,這里我貼出的是部分關鍵源碼:

public class OutputCacheAttribute : ActionFilterAttribute, IExceptionFilter 
    {

        private OutputCacheParameters _cacheSettings = new OutputCacheParameters { VaryByParam = "*" }; 
        //_cacheSettings中的每個屬性都以  CacheProfile 屬性的方式再包裝了一遍 
        //其余類似的屬性我就不貼出來了 
        public string CacheProfile 
        { 
            get{return _cacheSettings.CacheProfile ?? String.Empty; } 
            set{_cacheSettings.CacheProfile = value;} 
        } 
        //省去了無關代碼 
        public override void OnResultExecuting(ResultExecutingContext filterContext) 
        { 
                //省去了無關代碼 
                
                using (OutputCachedPage page = new OutputCachedPage(_cacheSettings)) 
                { 
                    page.ProcessRequest(HttpContext.Current); 
                } 
        } 
        private sealed class OutputCachedPage : Page 
        { 
            private OutputCacheParameters _cacheSettings; 
            public OutputCachedPage(OutputCacheParameters cacheSettings) 
            { 
                // Tracing requires Page IDs to be unique. 
                ID = Guid.NewGuid().ToString(); 
                _cacheSettings = cacheSettings; 
            } 
            protected override void FrameworkInitialize() 
            { 
                // when you put the <%@ OutputCache %> directive on a page, the generated code calls InitOutputCache() from here 
                base.FrameworkInitialize(); 
                InitOutputCache(_cacheSettings); 
            } 
        } 
        } 

由上面的代碼可以看出_cacheSettings根本就沒從配置文件里讀取,而是直接由 OutputCache( Duration=180,VaryByParam="none") 這樣設置進去的,然后直接傳人Page.InitOutputCache(cacheSettings)中,可見它直接使用asp.net以前的方式OutputCacheModule來實現緩存的,InitOutputCache中應該有去加載配置里面的設置, 然后看看cacheSettings的相應字段是否已經有合適的值了,如果沒有則使用配置里邊的值。而我們在使用OutputCache時,如果沒有傳人VaryByParam值,則它默認的值就為"*"(從cacheSettings初始化時看出來的) 。故代碼:

    [OutputCache(CacheProfile = "faceProfile")] 
    public ActionResult Index() 
    { 
        return View(); 
    } 

相當於

    [OutputCache(CacheProfile = "faceProfile",VaryByParam = "*")] 
    public ActionResult Index() 
    { 
        return View(); 
    } 

配置里的VaryByParam的值是不會覆蓋代碼里面的VaryByParam的值的,所以才會出現我們測試時,對每個參數都進行緩存的情況。

解決辦法:

  1. 把VaryByParam 就直接寫入代碼, 其他的參數在配置中來完成如:OutputCache(CacheProfile = "faceProfile", VaryByParam = "none")
  2. 自己實現一個OutputCacheAttritube
    下面就是我把OutputCacheAttritube源碼做一些修改后滿足自己需求的MySimpleCacheAttribute,不過它不適用於ChildAction,但我可以在配置文件中控制VaryByParam參數,改造后的代碼如下:
namespace Mvc.Cache 
{ 
    using System.Web.Mvc; 
    using System.Web.UI; 
    using System; 
    using System.Web; 
    public class MySimpleCacheAttribute : ActionFilterAttribute 
    { 
        private OutputCacheParameters _cacheSettings = new OutputCacheParameters(); 
        public MySimpleCacheAttribute() 
        { 
        } 
        public string CacheProfile 
        { 
            get 
            { 
                return _cacheSettings.CacheProfile ?? String.Empty; 
            } 
            set 
            { 
                _cacheSettings.CacheProfile = value; 
            } 
        }

        internal OutputCacheParameters CacheSettings 
        { 
            get 
            { 
                return _cacheSettings; 
            } 
        } 
        public int Duration 
        { 
            get 
            { 
                return _cacheSettings.Duration; 
            } 
            set 
            { 
                _cacheSettings.Duration = value; 
            } 
        } 
        public OutputCacheLocation Location 
        { 
            get 
            { 
                return _cacheSettings.Location; 
            } 
            set 
            { 
                _cacheSettings.Location = value; 
                
            } 
        } 
        public bool NoStore 
        { 
            get 
            { 
                return _cacheSettings.NoStore; 
            } 
            set 
            { 
                _cacheSettings.NoStore = value; 
                
            } 
        } 
        public string SqlDependency 
        { 
            get 
            { 
                return _cacheSettings.SqlDependency ?? String.Empty; 
            } 
            set 
            { 
                _cacheSettings.SqlDependency = value; 
            } 
        } 
        public string VaryByContentEncoding 
        { 
            get 
            { 
                return _cacheSettings.VaryByContentEncoding ?? String.Empty; 
            } 
            set 
            { 
                _cacheSettings.VaryByContentEncoding = value; 
            } 
        } 
        public string VaryByCustom 
        { 
            get 
            { 
                return _cacheSettings.VaryByCustom ?? String.Empty; 
            } 
            set 
            { 
                _cacheSettings.VaryByCustom = value; 
            } 
        } 
        public string VaryByHeader 
        { 
            get 
            { 
                return _cacheSettings.VaryByHeader ?? String.Empty; 
            } 
            set 
            { 
                _cacheSettings.VaryByHeader = value; 
            } 
        } 
        public string VaryByParam 
        { 
            get 
            { 
                return _cacheSettings.VaryByParam ?? String.Empty; 
            } 
            set 
            { 
                _cacheSettings.VaryByParam = value; 
            } 
        } 
        public override void OnResultExecuting(ResultExecutingContext filterContext) 
        { 
            if (filterContext == null) 
            { 
                throw new ArgumentNullException("filterContext"); 
            } 
            if (!filterContext.IsChildAction) 
            { 
                using (OutputCachedPage page = new OutputCachedPage(_cacheSettings)) 
                { 
                    page.ProcessRequest(HttpContext.Current); 
                } 
            } 
        }
        private sealed class OutputCachedPage : Page 
        { 
            private OutputCacheParameters _cacheSettings;

            public OutputCachedPage(OutputCacheParameters cacheSettings) 
            { 
                // Tracing requires Page IDs to be unique. 
                ID = Guid.NewGuid().ToString(); 
                _cacheSettings = cacheSettings; 
            }

            protected override void FrameworkInitialize() 
            { 
                base.FrameworkInitialize(); 
                InitOutputCache(_cacheSettings); 
            } 
        } 
    } 
} 

使用:

    [MySimpleCache(CacheProfile = "faceProfile")] 
    public ActionResult Index() 
    { 
        return View(); 
    } 


免責聲明!

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



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