程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> ASP.NET >> 關於ASP.NET >> asp.net mvc Partial OutputCache在SpaceBuilder中的應用實踐

asp.net mvc Partial OutputCache在SpaceBuilder中的應用實踐

編輯:關於ASP.NET

最近給SpaceBuilder增加OutputCache 時發現了一些問題,貼在這做個備忘,也方便遇到類似問題的朋友查閱。

目前SpaceBuilder 表現層使用是asp.net mvc v1.0,使用了很多RenderAction(關於asp.net mvc的Partial Requests參見Partial Requests in ASP.NET MVC) 。希望對於實時性要求不高的內容區域采用客戶端緩存來提升性能同時也彌補一下RenderAction對性能的損失。

使用asp.net mvc自帶 的OutputCache Filter時發現了一個可怕的bug,在View中任何一個RenderAction設置OutputCache卻影響了整個View。搜索發現確實是asp.net mvc目前已知的一個bug ,關於該問題的解決也有很多人提出了自己的方法。

關於asp.net mvc的緩存,Haacked寫了兩篇文章:

Donut Caching in ASP.NET MVC 介紹的是緩存整個頁面,對於一部分內容禁用緩存,是在mvc中實現的WebForm的Substitution功能。 存在以下弊端:當前一個View中有多個區域需要禁用緩存時使用比較麻煩,另外不能實現對頁面的不同的區域使用不同的過期策略。

Donut Hole Caching in ASP.NET MVC介 紹的是我想要的功能,即只緩存頁面的部分區域。但是弊端也非常明顯:只能通過WebForm中 的聲明方式來使用用戶控件(:),現在已經有點不適應這種方 式了,而且必須使用WebFormViewEngine),無法直接使用RenderPartial,而且 還必須設置強類型的ViewPage,確保在用 戶控件中的Model與View中的Model相同。使用太麻煩,限制也多。

Maarten Balliauw在 Creating an ASP.NET MVC OutputCache ActionFilterAttribute 和Extending ASP.NET MVC OutputCache ActionFilterAttribute - Adding substitution   也提出了一個完整的OutputCache解決方案。但是經測試啟用客戶端緩存時同樣會產生與RenderAction同樣的問題,還沒有 時間徹查這個問題,先把客戶端緩存禁用,暫時使用服務器端緩存應付一陣。

以Maarten Balliauw的代碼為原型,編寫了SpaceBuilder 的ActionOutputCacheAttribute:

public class ActionOutputCacheAttribute : ActionFilterAttribute
    {
        private static MethodInfo _switchWriterMethod = typeof(HttpResponse).GetMethod("SwitchWriter", BindingFlags.Instance | BindingFlags.NonPublic);

        public ActionOutputCacheAttribute(int cacheDuration)
        {
            _cacheDuration = cacheDuration;
        }

        //目前還不能設置為Client緩存,會與OutputCache同樣的問題
        private CachePolicy _cachePolicy = CachePolicy.Server;
        private int _cacheDuration;
        private TextWriter _originalWriter;
        private string _cacheKey;

        public override void OnActionExecuting (ActionExecutingContext filterContext)
        {
            // Server-side caching?
            if (_cachePolicy == CachePolicy.Server || _cachePolicy == CachePolicy.ClientAndServer)
            {
                _cacheKey = GenerateCacheKey(filterContext);
                CacheContainer cachedOutput = (CacheContainer)filterContext.HttpContext.Cache[_cacheKey];
                if (cachedOutput != null)
                {
                    filterContext.HttpContext.Response.ContentType = cachedOutput.ContentType;
                    filterContext.Result = new ContentResult { Content = cachedOutput.Output };
                }
                else
                {
                    StringWriter stringWriter = new StringWriterWithEncoding (filterContext.HttpContext.Response.ContentEncoding);
                    HtmlTextWriter newWriter = new HtmlTextWriter(stringWriter);
                    _originalWriter = (TextWriter)_switchWriterMethod.Invoke(HttpContext.Current.Response, new object[] { newWriter });
                }
            }
        }

        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            // Server-side caching?
            if (_cachePolicy == CachePolicy.Server || _cachePolicy == CachePolicy.ClientAndServer)
            {
                if (_originalWriter != null) // Must complete the caching
                {
                    HtmlTextWriter cacheWriter = (HtmlTextWriter)_switchWriterMethod.Invoke (HttpContext.Current.Response, new object[] { _originalWriter });
                    string textWritten = ((StringWriter)cacheWriter.InnerWriter).ToString();
                    filterContext.HttpContext.Response.Write(textWritten);
                    CacheContainer container = new CacheContainer(textWritten, filterContext.HttpContext.Response.ContentType);
                    filterContext.HttpContext.Cache.Add(_cacheKey, container, null, DateTime.Now.AddSeconds(_cacheDuration), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
                }
            }
        }

        private string GenerateCacheKey(ActionExecutingContext filterContext)
        {
            StringBuilder cacheKey = new StringBuilder("OutputCacheKey:");

             // Controller + action
            cacheKey.Append(filterContext.Controller.GetType().FullName.GetHashCode());
            if (filterContext.RouteData.Values.ContainsKey("action"))
            {
                cacheKey.Append("_");
                cacheKey.Append(filterContext.RouteData.Values["action"].ToString());
            }

            foreach (KeyValuePair<string, object> pair in filterContext.ActionParameters)
            {
                cacheKey.Append("_");
                cacheKey.Append(pair.Key);
                cacheKey.Append("=");

                if (pair.Value != null)
                    cacheKey.Append(pair.Value.ToString());
                else
                    cacheKey.Append(string.Empty);
            }

            return cacheKey.ToString();
        }

        private class CacheContainer
        {
            public string Output;
            public string ContentType;
            public CacheContainer(string data, string contentType)
            {
                Output = data;
                ContentType = contentType;
            }
        }

        public enum CachePolicy
        {
            NoCache = 0,
            Client = 1,
            Server = 2,
            ClientAndServer = 3
        }
    }

     {
 encoding;

StringWriterWithEncoding

public class StringWriterWithEncoding : StringWriter
    {
        Encoding encoding;
        public StringWriterWithEncoding(Encoding encoding)
        {
            this.encoding = encoding;
        }
        public override Encoding Encoding
        {
            get { return encoding; }
        }
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved