Custom cache not working at Child Actions-Collection of common programming errors


  • jbl

    I’m working with a custom class that output cche for mobile and normal pages seperately. I only caches index page. It works perfect while caching with index page. But when using with child actions, the following error is thrown:

    Server Error in '/' Application.
    
    Unable to cast object of type 'System.IO.StringWriter' to type 'System.Web.HttpWriter'.
    
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
    
    Exception Details: System.InvalidCastException: Unable to cast object of type 'System.IO.StringWriter' to type 'System.Web.HttpWriter'.
    
    Source Error: 
    
    
    Line 82:                         sw = new StringWriter(sb);
    Line 83:                         tw = new HtmlTextWriter(sw);
    Line 84:                         output = (HttpWriter)filterContext.RequestContext.HttpContext.Response.Output;
    Line 85:                         filterContext.RequestContext.HttpContext.Response.Output = tw;
    Line 86:                     }
    
    Source File: H:\Yazilimlar\Haber Sitesi v2\HaberSitesiV2\HaberSitesiV2\Filters\CustomCacheAttribute.cs    Line: 84 
    
    Stack Trace: 
    
    
    [InvalidCastException: Unable to cast object of type 'System.IO.StringWriter' to type 'System.Web.HttpWriter'.]
       HaberSitesiV2.Filters.CustomCacheAttribute.OnActionExecuting(ActionExecutingContext filterContext) in H:\Yazilimlar\Haber Sitesi v2\HaberSitesiV2\HaberSitesiV2\Filters\CustomCacheAttribute.cs:84
       System.Web.Mvc.Async.AsyncControllerActionInvoker.InvokeActionMethodFilterAsynchronously(IActionFilter filter, ActionExecutingContext preContext, Func`1 nextInChain) +69
       System.Web.Mvc.Async.c__DisplayClass3b.b__35() +21
       System.Web.Mvc.Async.AsyncControllerActionInvoker.InvokeActionMethodFilterAsynchronously(IActionFilter filter, ActionExecutingContext preContext, Func`1 nextInChain) +489
       System.Web.Mvc.Async.c__DisplayClass3b.b__35() +21
       System.Web.Mvc.Async.AsyncControllerActionInvoker.InvokeActionMethodFilterAsynchronously(IActionFilter filter, ActionExecutingContext preContext, Func`1 nextInChain) +489
       System.Web.Mvc.Async.c__DisplayClass3b.b__35() +21
       System.Web.Mvc.Async.c__DisplayClass37.b__31(AsyncCallback asyncCallback, Object asyncState) +191
       System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +130
       System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters, AsyncCallback callback, Object state) +197
       System.Web.Mvc.Async.c__DisplayClass25.b__1e(AsyncCallback asyncCallback, Object asyncState) +446
       System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +130
       System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +302
       System.Web.Mvc.c__DisplayClass1d.b__17(AsyncCallback asyncCallback, Object asyncState) +30
       System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +130
       System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +382
       System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +130
       System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +317
       System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +15
       System.Web.Mvc.c__DisplayClass8.b__2(AsyncCallback asyncCallback, Object asyncState) +71
       System.Web.Mvc.Async.WrappedAsyncResult`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +130
       System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +249
       System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +50
       System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +16
       System.Web.Mvc.c__DisplayClass7.b__6() +31
       System.Web.Mvc.ServerExecuteHttpHandlerWrapper.Wrap(Func`1 func) +27
       System.Web.Mvc.ServerExecuteHttpHandlerAsyncWrapper.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) +98
       System.Web.HttpServerUtility.ExecuteInternal(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage, VirtualPath path, VirtualPath filePath, String physPath, Exception error, String queryStringOverride) +2019
       System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage) +76
       System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +28
       System.Web.HttpServerUtilityWrapper.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +19
       System.Web.Mvc.Html.ChildActionExtensions.ActionHelper(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues, TextWriter textWriter) +483
       System.Web.Mvc.Html.ChildActionExtensions.Action(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues) +83
       System.Web.Mvc.Html.ChildActionExtensions.Action(HtmlHelper htmlHelper, String actionName, String controllerName) +10
       ASP._Page_Views_Anasayfa_Index_cshtml.Execute() in h:\Yazilimlar\Haber Sitesi v2\HaberSitesiV2\HaberSitesiV2\Views\Anasayfa\Index.cshtml:29
       System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +197
       System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +97
       System.Web.WebPages.StartPage.RunPage() +17
       System.Web.WebPages.StartPage.ExecutePageHierarchy() +62
       System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +76
       System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +260
       System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +115
       System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +295
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +13
       System.Web.Mvc.c__DisplayClass1a.b__17() +23
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +242
       System.Web.Mvc.c__DisplayClass1c.b__19() +21
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +242
       System.Web.Mvc.c__DisplayClass1c.b__19() +21
       System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +177
       System.Web.Mvc.Async.c__DisplayClass2a.b__20() +89
       System.Web.Mvc.Async.c__DisplayClass25.b__22(IAsyncResult asyncResult) +102
       System.Web.Mvc.Async.WrappedAsyncResult`1.End() +57
       System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +43
       System.Web.Mvc.c__DisplayClass1d.b__18(IAsyncResult asyncResult) +14
       System.Web.Mvc.Async.c__DisplayClass4.b__3(IAsyncResult ar) +23
       System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
       System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +57
       System.Web.Mvc.Async.c__DisplayClass4.b__3(IAsyncResult ar) +23
       System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
       System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +47
       System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
       System.Web.Mvc.c__DisplayClass8.b__3(IAsyncResult asyncResult) +25
       System.Web.Mvc.Async.c__DisplayClass4.b__3(IAsyncResult ar) +23
       System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
       System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +47
       System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
       System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9629708
       System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
    
    Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.17929
    

    And my class is follow.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Web;
    using System.Web.Caching;
    using System.Web.Mvc;
    using System.Text.RegularExpressions;
    using System.Reflection;
    using System.Web.UI;
    using System.Web.WebPages;
    
    namespace HaberSitesiV2.Filters
    {
        public enum CachePolicy
        {
            NoCache = 0,
            Client = 1,
            Server = 2,
            ClientAndServer = 3
        }
    
        public class CustomCacheAttribute : ActionFilterAttribute
        {
            #region Public properties
    
            public string VaryByParam { get; set; }
            public CachePolicy CachePolicy { get; set; }
    
            #endregion
    
            #region Private members
    
            private HtmlTextWriter tw;
            private StringWriter sw;
            private StringBuilder sb;
            private HttpWriter output;
            private bool cacheHitNormal = false;
            private bool cacheHitMobile = false;
            #endregion
    
            #region ActionFilterAttribute overrides
    
    
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                // Server-side caching?
                if (CachePolicy == CachePolicy.Server || CachePolicy == CachePolicy.ClientAndServer)
                {
                    if (filterContext.HttpContext.Application["cacheSql"] != null)
                    {
                        // Fetch cached data
                        string key = GenerateKey(filterContext);
                        object cachedData = HttpContext.Current.Cache.Get(key);
                        if (cachedData != null)
                        {
                            // cache hit
                            if (filterContext.HttpContext.GetOverriddenBrowser().IsMobileDevice)
                                cacheHitMobile = true;
                            else
                            cacheHitNormal = true;
                            // Return cached data
                            filterContext.HttpContext.Response.Write(cachedData);
                            filterContext.Result = new EmptyResult();
                        }
                        else
                        {
                            // Cache not hit.
                            if (filterContext.HttpContext.GetOverriddenBrowser().IsMobileDevice)
                                cacheHitMobile = false;
                            else
                            cacheHitNormal = false;
                            //
                            sb = new StringBuilder();
                            sw = new StringWriter(sb);
                            tw = new HtmlTextWriter(sw);
                            output = (HttpWriter)filterContext.RequestContext.HttpContext.Response.Output;
                            filterContext.RequestContext.HttpContext.Response.Output = tw;
                        }
                    }
                }
            }
    
            public override void OnResultExecuted(ResultExecutedContext filterContext)
            {
                // Server-side caching?
                if (CachePolicy == CachePolicy.Server|| CachePolicy == CachePolicy.ClientAndServer)
                {
                    if (filterContext.HttpContext.Application["cacheSql"] != null)
                    {
                        if (filterContext.HttpContext.GetOverriddenBrowser().IsMobileDevice ? !cacheHitMobile : !cacheHitNormal)
                        {
                            int duration = 0;
                                duration = Convert.ToInt32(filterContext.HttpContext.Application["cacheSql"]);
                            //response processing
                            output.Write(sb.ToString());
                            // Add data to cache
                            HttpContext.Current.Cache.Insert(
                                GenerateKey(filterContext),
                                sb.ToString(),
                                null,
                                DateTime.Now.AddMinutes(duration),
                                Cache.NoSlidingExpiration,
                                CacheItemPriority.Normal,
                                null);
                        }
                  }
                }
            }
            #endregion
    
            #region Helper methods
            private string GenerateKey(ControllerContext filterContext)
            {
                StringBuilder cacheKey = new StringBuilder();
    
                // Controller + action
                cacheKey.Append(filterContext.Controller.GetType().FullName);
                if (filterContext.RouteData.Values.ContainsKey("action"))
                {
                    cacheKey.Append("_");
                    cacheKey.Append(filterContext.RouteData.Values["action"].ToString());
                }
    
                // Variation by parameters
                List varyByParam = VaryByParam.Split(';').ToList();
    
                if (!string.IsNullOrEmpty(VaryByParam))
                {
                    foreach (KeyValuePair pair in filterContext.RouteData.Values)
                    {
                        if (VaryByParam == "*" || varyByParam.Contains(pair.Key))
                        {
                            cacheKey.Append("_");
                            cacheKey.Append(pair.Key);
                            cacheKey.Append("=");
                            cacheKey.Append(pair.Value.ToString());
                        }
                    }
                }
                cacheKey.Append("_");
                cacheKey.Append(filterContext.HttpContext.GetOverriddenBrowser().IsMobileDevice ? "mobil" : "normal");
                return cacheKey.ToString();
            }
    
            #endregion
        }
    }