ASP.NET MVC中的"Donut缓存"和TempData

6

有没有解决方案可以在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 委托以生�输出。 然�,它��应添加一个�代缓冲区,该缓冲区�留在未�请求时�调用的委托。 最�,它将客户端缓存能力�“公共��级为“仅�务器�,确�对页�的未�请求通过�在客户端上缓存��新调用委托。
���说,由�没有“正常�的请求生命周期,所以委托无法访问 Session �性(SessionStateTempDataProvider 将 TempData 存储在会�中)。我�解它是在 HttpApplication.ResolveRequestCache/HttpApplication.PostResolveRequestCache 事件中处�的,但当�状�是在 HttpApplication.AcquireRequestState 事件中��的(http://msdn.microsoft.com/en-us/library/ms178473.aspx)
也许我需���“高级自定义 TempDataProvider�🙂 有什么想法�?

好奇。上面那个助手的应用是什么?向客户端发送错误/状态消息? - Matt Kocaj
cottsak,你是对的。在完全缓存的页面上向不同的用户显示状态消息。 - eu-ge-ne
1个回答

6
我找到了解决方案:
主要思路是将TempData的副本保存在缓存中,并在每个请求中检索它。解决方案是自定义TempDataProvider和简单的http模块的组合。此外还有几个帮助程序和静态类。
以下是代码:
CustomTempDataProvider(自定义TempDataProvider):
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;
    }
}

Post-cache替换的辅助程序:

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>

如果你懂俄语,可以阅读这篇文章

希望这能有所帮助


你应该使用HttpRuntime.Cache来访问缓存,但你为什么要将它存储在那里呢?如果缓存是 out-of-proc 的并且tempData包含了非可序列化的内容,那怎么办呢?看起来这种情况很少见。 - IDisposable

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接