The Site Doctor Blog

Footprints in the snow of a warped mind

< Back to Blog

Using ModelStateToTempData in Umbraco

For those of you who have been working with MVC for a while you may have come across the awesome attribute from Jeremy Skinner "ModelStateToTempData" in MVCContrib (http://mvccontrib.codeplex.com/). In short, it stores the ModelState (i.e. errors) in a temporary data store so when you redirect the user to the original action, their form's data is persisted. This means you can write very clean controllers rather than always returning View(postedData).

I for one am a massive fan of this however it doesn't work in Umbraco as they use their own RedirectResult "RedirectToUmbracoPageResult" which means ModelStateToTempData doesn't pickup that it needs to rehydrate the view model so I've tweaked with the original ever so slightly to make it work in Umbraco.

Enjoy!

Download the DLL in a Zip File

To use it, you just need to mark up your Actions:

[ModelStateToTempData]
public ActionResult YourModelName(){
    var vm = new YourModelNameViewModel();
    return View(vm);
}

[HttpPost]
[ModelStateToTempData]
public ActionResult YourModelName(YourModelNameViewModel data){
    if (!ModelState.IsValid)
        return RedirectToCurrentUmbracoPage(); // Redirect back to the form safe in the knowledge the data is persisted

     /* Do Stuff Here */
}

 

Here's the complete source:

using System.Web.Mvc;

using Umbraco.Web.Mvc;

public class ModelStateToTempDataAttribute : ActionFilterAttribute
{
    public const string TempDataKey = "__TSD_ValidationFailures__";

    /// <summary>
    /// When a RedirectToRouteResult is returned from an action, anything in the ViewData.ModelState dictionary will be copied into TempData.
    /// When a ViewResultBase is returned from an action, any ModelState entries that were previously copied to TempData will be copied back to the ModelState dictionary.
    /// </summary>
    /// <param name="filterContext"></param>
    public override void OnActionExecuted( ActionExecutedContext filterContext)
    {
        var modelState = filterContext.Controller.ViewData.ModelState;

        var controller = filterContext.Controller;

        var result = filterContext.Result;

        if (result is ViewResultBase)
        {
            //If there are failures in tempdata, copy them to the modelstate
            CopyTempDataToModelState(controller.ViewData.ModelState, controller.TempData);
            return;
        }

        //If we're redirecting and there are errors, put them in tempdata instead (so they can later be copied back to modelstate)
        if ((result is RedirectToRouteResult || result is RedirectResult || result is RedirectToUmbracoPageResult ) && !modelState.IsValid)
        {
            CopyModelStateToTempData(controller.ViewData.ModelState, controller.TempData);
        }
    }

    private void CopyTempDataToModelState( ModelStateDictionary modelState, TempDataDictionary tempData)
    {
        if (!tempData.ContainsKey(TempDataKey)) return;

        var fromTempData = tempData[TempDataKey] as ModelStateDictionary;
        if (fromTempData == null) return;

        foreach ( var pair in fromTempData)
        {
            if (modelState.ContainsKey(pair.Key))
            {
                modelState[pair.Key].Value = pair.Value.Value;

                foreach ( var error in pair.Value.Errors)
                {
                    modelState[pair.Key].Errors.Add(error);
                }
            }
            else
            {
                modelState.Add(pair.Key, pair.Value);
            }
        }
    }

    private static void CopyModelStateToTempData( ModelStateDictionary modelState, TempDataDictionary tempData)
    {
        tempData[TempDataKey] = modelState;
    }
}
Author: Tim on

Liked this post? Got a suggestion? Leave a comment