Is there any solution for accessing TempData property in the HttpResponseBase.WriteSubstitution() method
This does not work:
<%= Response.WriteSubstitution(x => Html.Encode(TempData["message"].ToString())) %>
But this works:
<%= Response.WriteSubstitution(x => DateTime.Now.ToString()) %>
The problem is in request processing for once cached pages. According to http://msdn.microsoft.com/en-us/library/system.web.httpresponse.writesubstitution.aspx:
On the first request to the page, the WriteSubstitution calls the HttpResponseSubstitutionCallback delegate to produce the output. Then, it adds a substitution buffer to the response, which retains the delegate to call on future requests. Finally, it degrades client-side cacheability from public to server-only, ensuring future requests to the page re-invoke the delegate by not caching on the client.
In other words the delegate does not have access to Session property (SessionStateTempDataProvider stores TempData in session) because there is no “normal” request life cycle. As I understand it is processed at HttpApplication.ResolveRequestCache/HttpApplication.PostResolveRequestCache event, but the current state is acquired at the HttpApplication.AcquireRequestState event (http://msdn.microsoft.com/en-us/library/ms178473.aspx)
Maybe I need some kind of “advanced custom TempDataProvider” 🙂 Any ideas?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
I’ve found the solution:
The main idea is in saving copy of TempData in the Cache and retreiving it on every request. The solution is a combination of custom TempDataProvider and simple http module. Plus there are couple of helpers and static classes.
Here is the code:
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()
{
}
}
Register it in the 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;
}
}
register it in the Global.asax:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
}
Static class for accessing 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 for Post-cache Substitution:
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;
}
}
Now this works successfully:
<h3><%= Html.MvcResponseSubstitute(context => CustomTempData.Get("message")) %></h3>
If you understand russian, read this
Hope this helps
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0