DotNetCurry Logo

Using ASP.NET MVC TempData and Session to pass values across Requests

Posted by: Mahesh Sabnis , on 1/19/2015, in Category ASP.NET MVC
Views: 162540
Abstract: In ASP.NET MVC business applications, you may need to maintain the state of the data across requests. This article demonstrates how to use TempData and Session to achieve this requirement.

ASP.NET has numerous useful features and one of it is state management. ASP.NET Web Form developers have the habit of using Session objects for passing values across pages. Since in ASP.NET, everything is controlled by Page, the use of various state management techniques like ViewState, Cookies, Sessions, etc. play an important role here. But unlike ASP.NET Web Forms, in ASP.NET MVC the Controller decides what values are accepted from a view and which view to send the response to. The Controller in MVC is the major component for request processing and has its own lifecycle. Consider a scenario where one controller has an action method which returns the action method for another controller. During this controller transition, value entered from one controller needs to be used by another controller for some processing. In this case, it is mandatory for the developer to make use of some mechanism to maintain the state of the value across various controllers. 

 

In MVC the following image explains the scenario:

mvc-controller-cycle

Here the question is how to implement this? Since ASP.NET MVC builds on the top of the ASP.NET framework, we have access to the Session objects derived from HttpSessionBase. Like ASP.NET WebForms, in MVC  the Session property is provided by HttpContext class. But MVC has its own TempData object which is used to pass data across controllers. This is derived from TempDataDictionary. The TempData has a very short life and sets itself to null when the target view is fully loaded. Here are some features of TempData and Session objects.

TempData:

  • is the property of the ControllerBase class
  • is used to pass data from one controller to other
  • its life is ended when the target view is loaded completely
  • it is recommended to type-cast the value when TempData value is used for processing
  • Mostly used to store Validation Error Messages, etc.

The Life time of the TempData can be managed using the Keep() method.

Session:

  • is used to pass data across controllers in MVC Application.
  • Session data never expires.

Like TempData the value must be type-casted when it is used for processing.

Implementating TempData

Step 1: Download the free Visual Studio 2013 Community Edition (the article uses VS2013 Ultimate with Update 4) and create a new empty MVC application of the  name MVC5_Sessions. In this project in App_Data folder add a new Sql Server database of the name Application. In this database add the following tables:

Category

CREATE TABLE [dbo].[Category]
(
    CategoryId int primary key Identity,
    CategoryName varchar(100) Not Null
)

Product

CREATE TABLE [dbo].[Product]
(
    ProductId INT NOT NULL PRIMARY KEY identity,
    ProductName varchar(100) Not Null,
    UnitPrice decimal Not Null,
    CategoryId int References Category (CategoryId)
)

Add some sample data in these tables.

Step 2: In the Models folder add a new ADO.NET Entity Data Model. In the wizard select the Application database we added in Step 1. Select all tables from the database. After completing the wizard, the following mapping will be displayed:

category-product

Using TempData to pass values across controllers

Step 3: In the Controllers add a new Empty MVC Controller of the name CategoryController .In this controller, we already have an Index method along with the add SerachProducts action method with HttpGet and HttpPost as below:

using System.Linq;
using System.Web.Mvc;

using MVC5_Sessions.Models;

namespace MVC5_Sessions.Controllers
{
public class CategoryController : Controller
{
    ApplicationEntities ctx;

    public CategoryController()
    {
        ctx = new ApplicationEntities();
    }

    // GET: Category
    public ActionResult Index()
    {
        var cats = ctx.Categories.ToList();
        return View(cats);
    }

    public ActionResult SearchProducts()
    {
        return View();
    }


    [HttpPost]
    public ActionResult SearchProducts(Category c)
    {
       TempData["Category"] = c;
        return  RedirectToAction("Index","Product");
    }
}
}

The above code has the SearchProducts action method for [HttpPost]. This method declares the TempData object with key as ‘Category’. This object is set to the Category object passed to the SearchProducts action method. The SearchProducts action method returns the Index method of the ProductController using the below statement:

return  RedirectToAction("Index","Product");

Step 3: Scaffold Index.cshtml and SearchProducts.cshtml from the Index and Search Products action method. Change the SearchProducts.cshtml to the following:

@model MVC5_Sessions.Models.Category

@{
    ViewBag.Title = "SearchProducts";
}

<h2>SearchProducts</h2>


@using (Html.BeginForm()) 
{
    @Html.EditorFor(cat=>cat.CategoryId);
     
         <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Search" class="btn btn-default" />
            </div>
        </div>
}

The above view has a textbox which is bound with the CategoryId of the Category Model class.

Step 4: In the Controllers folder, add a new Empty MVC controller of the name ProductController and add the following code in it:

using System.Linq;
using System.Web.Mvc;
using MVC5_Sessions.Models;

namespace MVC5_Sessions.Controllers
{
    public class ProductController : Controller
    {

        ApplicationEntities ctx;
        public ProductController()
        {
            ctx = new ApplicationEntities(); 
        }

        // GET: Product
        /// <summary>
        /// Type Case the TempData to Category
        /// </summary>
        /// <returns></returns>
        public ActionResult Index()
        {
            if (TempData["Category"] != null)
            {
                var Cat = (Category)TempData["Category"];

                var Products = from p in ctx.Products
                               where p.CategoryId == Cat.CategoryId
                               select p;


                return View(Products.ToList());
            }
            else
            {
                return View(ctx.Products.ToList());
            }
        }

        [HttpPost]
        public ActionResult Index(int id=0) 
        {
            var Data = TempData["Category"] as Category;
          
            return RedirectToAction("Index");
        }
    }
}

The above code has Index action methods for HttpGet and HttpPost. The HttpGet Index method accepts the TempData[“Category”] object and type casts it to Category. The code further queries the Products based upon the CategoryId and returns Products under the category.

 

Step 5: Scaffold the Index.cshtml from the Index action method and make the following changes in it:

@model IEnumerable<MVC5_Sessions.Models.Product>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.ProductId)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ProductName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.UnitPrice)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Category.CategoryName)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.ProductId)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.ProductName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.UnitPrice)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Category.CategoryName)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.ProductId }) |
            @Html.ActionLink("Details", "Details", new { id=item.ProductId }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.ProductId })
        </td>
    </tr>
}

</table>

@using(Html.BeginForm())
{
    <input type="submit" value="Post" />
}

When the submit button is clicked, it posts to the Index action method with HttpPost. Apply breakpoint on the HttpPost Index action method to check the value in the TempData[“Category”] when the end-user posts to the Index view.

Run the Application and navigate to the SearchProducts action of the Category Controller:  http://<server>/Category/SearchProducts

The following view will be displayed:

search-view

Enter CategoryId in the TextBox and click on the ‘Search’ button. The Product Index view will be displayed:

product-view

Click on the ‘Post’ button which will enter in the debug-mode to the HttpPost Index action method. We have already applied the breakpoint on this Index method. Here check the value of the TempData[“Category”]. It will display the values in the TempData[“Category”] as zero (0) and the data will be null as shown here:

tempdata-debug

(Note: These are two different images showing debug from LHS and RHS of this expression)

The value from the TempData[“Category”] is null because the TempData gets killed when the response is completely loaded.

Step 6: To maintain the life of the TempData[“Category”], make the following change in the HttpGet Index Action method of the ProductController:

public ActionResult Index()
{
    if (TempData["Category"] != null)
            {
        var Cat = (Category)TempData["Category"];
        var Products = from p in ctx.Products
                       where p.CategoryId == Cat.CategoryId
                       select p;

        TempData.Keep("Category");

        return View(Products.ToList());
    }
    else
    {
        return View(ctx.Products.ToList());
    }
}

The TempData.Keep(“Category”) will maintain the state of the data now even when the response view is loaded.

Step 7: Repeat Step 5. You will see the TempData[“Category”] is maintained as shown here:

debug-with-values

That’s it, in MVC we can make use of TempData to maintain the state of the values across the requests using which data can be passed across controllers.

Something very Important about TempData and Session

Since TempData makes use of the Session State behavior, it must be enabled on the controller using TempData. By default it is always enabled, however using code, the session state can be disabled on the controller. In the CategoryController class, add the following attribute:

[SessionState(System.Web.SessionState.SessionStateBehavior.Disabled)]
public class CategoryController : Controller
{

The above code disables the session state on request processing. Run the application and navigate to the Category/SearchProducts View, enter CategoryId in the TextBox, and click on the ‘Search’ button. The following page gets displayed:

session-tempdata

The above error clearly shows that ‘SessionStateTempDataProvider’ requires the Session State to be enabled.

The Same demo can be implemented using Session[“Category”] object to pass data across controllers.

Conclusion: While developing business applications using ASP.NET MVC, we may need to maintain the state of the data across requests. TempData is your friend in such scenarios.

Download the entire source code of this article (Github)

Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a DotNetCurry author and Microsoft MVP having over 17 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by VINOD YADAV on Tuesday, April 28, 2015 3:41 AM
vcx
Comment posted by Gulab Mehak on Thursday, April 30, 2015 7:56 AM
Really nice Mahesh!
really helpful

Cheers