代码之家  ›  专栏  ›  技术社区  ›  eu-ge-ne

ASP.NET MVC“甜甜圈缓存”和tempdata

  •  5
  • eu-ge-ne  · 技术社区  · 15 年前

    在httpResponseBase.WriteSubstitution()方法中是否有访问tempData属性的解决方案?

    这不起作用:

    <%= Response.WriteSubstitution(x => Html.Encode(TempData["message"].ToString())) %>
    

    但这是可行的:

    <%= Response.WriteSubstitution(x => DateTime.Now.ToString()) %>
    

    对于一次缓存的页面,请求处理中存在问题。根据 http://msdn.microsoft.com/en-us/library/system.web.httpresponse.writesubstitution.aspx :

    在对页面的第一个请求上,writesubstitution调用httpResponseSubstitutionCallback委托以生成输出。然后,它在响应中添加了一个替换缓冲区,该缓冲区保留委托以调用未来的请求。最后,它降低了客户端从公共缓存到服务器的可缓存性,确保将来对页面的请求通过不在客户端上缓存来重新调用委托。

    换句话说,委托无权访问会话属性(sessionstatetempdataprovider在会话中存储tempdata),因为没有“正常”的请求生命周期。据我所知,它是在httpapplication.resolverequestcache/httpapplication.postpresolverequestcache事件中处理的,但当前状态是在httpapplication.acquireRequestState事件中获取的。( http://msdn.microsoft.com/en-us/library/ms178473.aspx )

    也许我需要某种“高级定制tempdataprovider”:)有什么想法吗?

    1 回复  |  直到 15 年前
        1
  •  5
  •   Jeff Atwood    15 年前

    我找到了解决方案:

    主要的想法是将tempdata的副本保存在缓存中,并在每次请求时检索它。解决方案是自定义tempdataprovider和简单HTTP模块的组合。另外还有几个助手和静态类。

    代码如下:

    自定义临时数据提供程序:

    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.Web.Caching;
    using System.Web.Mvc;
    
    public class CustomTempDataProvider : SessionStateTempDataProvider, IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.BeginRequest += new EventHandler(application_BeginRequest);
        }
    
        void application_BeginRequest(object sender, EventArgs e)
        {
            var httpContext = HttpContext.Current;
            var tempData = httpContext.Cache[TempDataKey] ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
            httpContext.Items.Add("TempData", tempData);
            httpContext.Cache.Remove(TempDataKey);
        }
    
        public override void SaveTempData(ControllerContext controllerContext,
            IDictionary<string, object> values)
        {
            HttpContext.Current.Cache.Insert(TempDataKey, values, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, null);
            base.SaveTempData(controllerContext, values);
        }
    
        public static string TempDataKey
        {
            get
            {
                string sessionID = "0";
                var httpContext = HttpContext.Current;
                if(httpContext.Session != null)
                {
                    sessionID = httpContext.Session.SessionID;
                }
                else if (httpContext.Request.Cookies["ASP.NET_SessionId"] != null)
                {
                    sessionID = httpContext.Request.Cookies["ASP.NET_SessionId"].Value;
                }
                return "TempData-For-Session-" + sessionID;
            }
        }
    
        public void Dispose()
        {
        }
    }
    

    在web.config中注册:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.web>
            <httpModules>
                <add name="CustomTempDataProvider" type="CustomTempDataProvider" />
            </httpModules>
        </system.web>
        <system.webServer>
            <modules>
                <remove name="CustomTempDataProvider" />
                <add name="CustomTempDataProvider" type="CustomTempDataProvider" />
            </modules>
        </system.webServer>
    </configuration>
    

    自定义控制器工厂:

    using System.Web.Routing;
    using System.Web.Mvc;
    
    public class CustomControllerFactory : DefaultControllerFactory
    {
        public override IController CreateController(
            RequestContext requestContext, string controllerName)
        {
            var controller = (Controller)base.CreateController(requestContext, controllerName);
            controller.TempDataProvider = new CustomTempDataProvider();
            return controller;
        }
    }
    

    在global.asax中注册:

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    
        ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
    }
    

    用于访问tempdata的静态类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    public static class CustomTempData
    {
        public static object Get(string key)
        {
            var tempData = HttpContext.Current.Items["TempData"] as IDictionary<string, object>;
            var item = tempData.FirstOrDefault(x => x.Key == key).Value ?? String.Empty;
            return item;
        }
    }
    

    后缓存替换帮助程序:

    using System;
    using System.Web;
    using System.Web.Mvc;
    
    public static class Html
    {
        public delegate object MvcResponseSubstitutionCallback(HttpContextBase context);
    
        public static object MvcResponseSubstitute(this HtmlHelper html, MvcResponseSubstitutionCallback callback)
        {
            html.ViewContext.HttpContext.Response.WriteSubstitution(
                context =>
                    HttpUtility.HtmlEncode(
                        (callback(new HttpContextWrapper(context)) ?? String.Empty).ToString()
                    )
                );
    
            return null;
        }
    }
    

    现在这项工作成功:

    <h3><%= Html.MvcResponseSubstitute(context => CustomTempData.Get("message")) %></h3>
    

    如果你懂俄语,请阅读 this

    希望这有帮助