ASP.NET MVC offers TempData. TempData, as the name implies, is a temporary storage. It retains the data between two user requests but no more. By default, it uses the SessionState for storing data. A web farm has multiple servers having the application. Subsequent requests do not go to the same server. So, using session for storing TempData is not a viable solution. Fortunately for us, TempData is customisable. This post shows how to create a custom TempDataProvider.

Alternatives for Azure

We are developing our web application for Azure. Usually we store session data in a SQL Azure database. Using SQL Azure is more expensive. If we are not using SQL Azure for persistence, it makes sense to avoid using Session altogether.

Another option is to use the cache. In Azure, AppFabric caching is recommended. Again, this has an additional cost.

For small web applications, there is no need to use SQL Azure or AppFabric caching. But, sometimes, we need to use TempData. That is where our custom TempDataProvider is useful. We store this data in cookies. Cookies are sent in every request. So, it works for us.

Cookie TempDataProvider

Let us say, we perform a search. An example of TempData is our search criteria. We store the search criteria in a cookie. And retrieve it from the cookie. Our custom TempDataProvider facilitates this. ITempDataProvider interface has a LoadTempData method and SaveTempData method.

public class CookieTempDataProvider : ITempDataProvider
{      
    private const string TempDataCookie = "TempData";

    public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
    {
    }
       
    public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
    {        
    }
}

A custom TempDataProvider implements the ITempDataProvider interface. We start with implementing the SaveTempData method.

public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
    HttpResponseBase res = controllerContext.RequestContext.HttpContext.Response;
    HttpCookie cookie = res.Cookies.Get(TempDataCookie);
    if (cookie == null)
    {
        cookie = new HttpCookie(TempDataCookie);
        res.Cookies.Add(cookie);
    }

    foreach (KeyValuePair<string, object> kvp in values)
    {
        BinaryFormatter formatter = new BinaryFormatter();
        using (MemoryStream ms = new MemoryStream())
        {
            formatter.Serialize(ms, kvp.Value);
            byte[] bytes = ms.GetBuffer();
            string base64Value = Convert.ToBase64String(bytes);

            string keyExists = cookie.Values.Get(kvp.Key);
            if (keyExists != null)
            {
                cookie.Values.Set(kvp.Key, base64Value);
            }
            else
            {
                cookie.Values.Add(kvp.Key, base64Value);
            }
        }
    }
}

The cookie name is TempData. In the above code, we create a new cookie or use the existing cookie. Also, cookies store only string data. TempData is usually an object. We use the BinaryFormatter to serialize the object into a string. Set the cookie value to the Base64 string.

LoadTempData method does the reverse. We retrieve the TempData from the cookie.

public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
    HttpRequestBase req = controllerContext.RequestContext.HttpContext.Request;
    HttpCookie cookie = req.Cookies[TempDataCookie];

    Dictionary<string, object> tempData = new Dictionary<string, object>();
    if (cookie != null)
    {
        for (int keyIndex = 0; keyIndex < cookie.Values.Count; keyIndex++)
        {
            string key = cookie.Values.GetKey(keyIndex);
            if (!string.IsNullOrEmpty(key))
            {
                string base64Value = cookie.Values.Get(keyIndex);
                byte[] buffer = Convert.FromBase64String(base64Value);
                using (MemoryStream ms = new MemoryStream(buffer))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    object value = formatter.Deserialize(ms);
                    tempData.Add(key, value);
                }
            }
        }
    }

    return tempData;
}

Get the TempData cookie. From the cookie, convert each key value pair into a TempData dictionary. Deserialize the value from its Base 64 representation to an object.

Finally, add the TempDataProvider to the base controller.

public BaseController(ITempDataProvider tempDataProvider)
{
    this.TempDataProvider = tempDataProvider;
}

We are all set to use TempData in our Azure applications.

Related Posts

Leave a Reply

Your email address will not be published.