2009-03-25 3 views
5

Есть ли решение для доступа к TempData собственности в методе HttpResponseBase.WriteSubstitution()ASP.NET MVC "пончик кэширование" и 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» :) Всех идеи ?

+0

любопытным. какое приложение для помощника выше? сообщение об ошибке/статусе клиенту? –

+0

cottsak вы правы. Отображение сообщений о состоянии для разных пользователей на полностью кэшированных страницах –

ответ

5

Я нашел решение:

Основная идея заключается в сохранении копии TempData в кэше и retreiving его на каждом запросе. Решение представляет собой комбинацию пользовательского TempDataProvider и простого http-модуля. Кроме того, есть несколько помощников и статические классы.

Вот код:

CustomTempDataProvider:

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> 

CustomControllerFactory:

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; 
    } 
} 

зарегистрировать его в глобальном масштабе.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; 
    } 
} 

Helper для пост-кэша Замены:

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

Надеюсь, что это помогает

+0

очень приятно, спасибо за обмен этим кодом! –

+0

Вы должны использовать HttpRuntime.Cache, чтобы добраться до кеша ... но почему вы его даже там храните? Что делать, если кэш не работает, а tempData имеет несовместимый материал? Похоже на края. – IDisposable

Смежные вопросы