Home » ASP » ASP.MVC » How to save the ModelState into session following the good practice

How to save the ModelState into session following the good practice

Tim Barcz, Matt Hawley, Stephen Walther and Scott Guthrie (VP at Microsoft and lead for many project like Entity Framework, Asp.Net, etc) have already discussed about this problematic and created the PRG pattern to solve this problem. In fact, to solve this problem you should not handle manually the ModelState but simply use Import and Export attribute like the following example.

[AcceptVerbs(HttpVerbs.Get), ImportModelStateFromTempData]
public ActionResult MyAction(ModelObject myObject)
{
    return View();
}

[AcceptVerbs(HttpVerbs.Post), ExportModelStateToTempData]
public ActionResult MyActionSubmit(ModelObject  myObject)
{
   return View();
}

These attributes are not from the .Net framework and you need to have them inside your project by creating them. Once it’s done once, it’s done for the life time of your project.

First, you need to create the attributes. To do, you need to create a class that inherit the class ActionFilterAttribute. Since we are using 2 attributes that share the same information, we will create 3 classes. The first one will contain the sharing key for the session and the two others will be for the Import and Export.

public abstract class ModelStateTempDataTransfer : ActionFilterAttribute
{
    protected static readonly string Key = typeof(ModelStateTempDataTransfer).FullName;
}

Then, the class to export. Here you can add more custom code for your project. This version will all the ModelState only if this one contain errors.

public class ExportModelStateToTempData : ModelStateTempDataTransfer
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        //Only export when ModelState is not valid
        if (!filterContext.Controller.ViewData.ModelState.IsValid)
        {
            //Export if we are redirecting
            if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
            {
                filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState;
            }
        }

        base.OnActionExecuted(filterContext);
    }
}

The last class will import the ModelState. In fact, it will merge the new one with the old one in the session.

public class ImportModelStateFromTempData : ModelStateTempDataTransfer
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary;

        if (modelState != null)
        {
            //Only Import if we are viewing
            if (filterContext.Result is ViewResult)
            {
                filterContext.Controller.ViewData.ModelState.Merge(modelState);
            }
            else
            {
                //Otherwise remove it.
                filterContext.Controller.TempData.Remove(Key);
            }
        }

        base.OnActionExecuted(filterContext);
    }
}

As you can see, we do not use the session directly but we store everything into the TempData which use the session but handle the life cycle for us. This mean that it won’t stay for 20 minutes (default value of a session life time). It will stay until the next post back and be there if the request is redirected.

You can see it in the MVC open source project called MVCContrib (slightly modified version of this one). You can also find the source of the code in this blog post at this website.

If you like my article, think to buy my annual book, professionally edited by a proofreader. directly from me or on Amazon. I also wrote a TypeScript book called Holistic TypeScript

11 Responses so far.

  1. Joe Soloman says:

    Stolen almost verbatim from http://weblogs.asp.net/rashid/asp-net-mvc-best-practices-part-1#prg . You should really write your own articles

    • I never saw that article of my life and it is really not the same text… maybe you wrote that feedback on the wrong page? Concerning the code, this is a simply code which may look like MVCContrib which I mention in the article. I hope you got a better day than when you wrote than comment. Keep it cool and smile 🙂

  2. walera says:

    Very useful article, thanks!

  3. Nathalie Desrosiers says:

    I’m new to ASP.NET MVC. You write to add a class (in fact 3), but where we add them? In the Models folder ? In the Controller folder?

    Merci.

    • Hello,
      It’s great that you are touching Asp.Net MVC. This topic is not a beginner one. You need to be sure that you really want to go that path because you have a uncommon scenario. The class `ExportModelStateToTempData` and `ImportModelStateFromTempData` can be in any folder you want — it doesn’t matter. What matter is to have a logical place where only “framework classes” are together to try to avoid mixing your business logic classes with your framework ones.

  4. fernando says:

    Why I ghet an error on the KEY??

    the name ‘key’ does not exist int he curretn context

    • Do you get this error at this line: ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary;

      If yes, than, it can happen. The better approach, and more robust, is to look if TempData is having the key before trying to cast it.

      • fernando says:

        it is not a runtime exception, the project does not build

        mark as erro each line that contains ‘Key’ in the import/export classes

        ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary;
        filterContext.Controller.TempData.Remove(Key);

        filterContext.Controller.TempData[key] = filterContext.Controller.ViewData.ModelState;

      • fernando says:

        SORRY!!!

        IT WAS MYU FAULT

        YOUR CODE WORKS PERFECTLY

        really appreciate it!!

  5. H.Lotfy says:

    How can I do this in Asp.Net core 2?
    filterContext.Controller is an object and does not have TempData dictionary. I tried to use filterContext.HttpContext.Items
    dictionary but The collection’s contents are discarded after each request.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.