top button
Flag Notify
    Connect to us
      Site Registration

Site Registration

Understanding ViewData, ViewBag And TempData

+2 votes
367 views

While working with ASP.NET MVC applications you often need to pass data from controller action methods to the view. And there are various techniques to accomplish that goal - ViewData, ViewBag and TempData. This article explains these techniques, their usage and the difference between them.

In an ASP.NET MVC application a controller can easily pass a model to a view using an overloaded View() method as shown below:

public ActionResult Index()
{
  List<Customer> model = GetCustomers();
  return View(model);
}

As you can see the Index() method gets a List of Customer objects from a hypothetical GetCustomers() helper method and then passes it to the Index view by calling the View() method. The Index view can then access this model using the Model property.

Although passing data to a view through a model or view model class is a recommended practice, at times you need to pass some arbitrary data to the view. For example, you may need to pass a success or error message from a controller to a view so that it can be rendered in the browser. Or you may want to pass an auxiliary object holding some calculated results to the view. Since such data is not a part of the model you need some mechanism to pass it to the view. That is where ViewData, ViewBag and TempData objects come into picture. ViewData, ViewBag and TempData are inbuilt objects available to controllers and views. Each of these objects have their own specialties and features. So, let's examine each of them in bit more detail.

If you need to pass reasonable amount of data from a controller to a view that's not a part of model itself, you should give a thought to creating a view model. If something can't go in view model for some reason you can use the techniques discussed here to facilitate the data transfer.

ViewData

The primary purpose of ViewData is to carry data from the controller to the view. ViewData is a dictionary object and is of type ViewDataDictionary. Just like any other dictionary object in .NET, ViewData allows you to store key-value pairs. Data stored in ViewData object exists only during the current request. In other words, as soon as the view is rendered in the browser the ViewData object is emptied.

The following code shows an action method that sets a ViewData key named message with a developer defined message.

public ActionResult Index()
{
  Customer model = new Customer() { 
                   CustomerID = "ALFKI", 
                   CompanyName = "Company 1", 
                   ContactName = "Contact 1", 
                   Country = "Country 1" };
  ViewData["message"] = "This is a test message";
  return View(model);
}

The Index() action method creates an instance of Customer class. The Customer object thus created is supposed to act as the model for the Index view. Then the code stores message key in the ViewData dictionary with its value set to This is a test message. Finally, the action method returns Index view by calling View() method and passing Customer model to it.

The following code from the Index view shows how ViewData dictionary can be accessed inside a view.

@model ViewDataViewBagTempData.Models.Customer

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <h1>@Model.CustomerID</h1>
    <h3>@ViewData["message"]</h3>
    <h2>@ViewData.Model.CompanyName</h2>
</body>
</html>

Notice the code marked in bold letters. The @model sets the data model for the view to ViewDataViewBagTempData.Models.Customer. This data model can be accessed using Model property as shown by @Model.CustomerID line. Next, the code outputs the value of message ViewData key using the dictionary syntax. The last line marked in bold letters may surprise you. It uses Model property of ViewData object to access the model associated with the view. So, ViewData provides a pointer to the model although you will use it rarely in this fashion because the model is directly available to a view through Model property.

As soon as the request-response cycle completes the ViewData dictionary is emptied.

In the preceding example, you stored a string value in ViewData. You could have stored any other type of data such as integer, Boolean or object. The following code stores CustomerInfo object into ViewData:

public ActionResult Index()
{
  Customer model = new Customer() { ... };
  CustomerInfo info = new CustomerInfo() { 
                      LastOrderID = 100, 
                      LastOrderDate = new DateTime(2013, 01, 20), 
                      LastOrderAmount = 12345.6M };
  ViewData["extrainfo"] = info;
  return View(model);
}

The above code creates Customer model as before. Additionally, it creates an instance of CustomerInfo class. The CustomerInfo class has three properties - LastOrderID, LastOrderDate and LastOrderAmount. It then sets a ViewData key named extrainfo to this CustomerInfo object.

Whatever you store inside ViewData gets stored as a generic object and you must typecast it while retrieving it inside a view. The following code shows how:

<h3>@{
  CustomerInfo info = (CustomerInfo)ViewData["extrainfo"];
  @info.LastOrderID
  @: |  
  @info.LastOrderDate
  @: |  
  @info.LastOrderAmount
}</h3>

As you can see the above code residing in the Index view retrieves extrainfo from ViewData and typecasts it to CustomerInfo class. You can then use properties of CustomerInfo class to display their values in the view. Remember that you also need to check for null values in your view in case you expect ViewData entries to be null.

ViewBag

ViewBag is a wrapper over ViewData and allows you to store and retrieve values using object-property syntax rather than key-value syntax used by dictionary objects. It does so using the dynamic data type feature of .NET. In addition to providing object-properties syntax ViewBag also saves you from the job of typecasting as you did with ViewData object. Let's see how ViewBag can be used:

public ActionResult Index()
{
  Customer model = new Customer() { ... };
  CustomerInfo info = new CustomerInfo() { ... };
  ViewBag.ExtraInfo = info;
  return View(model);
}

As you can see, this time the code uses ViewBag object to store CustomerInfo object. It does so by declaring a developer defined property ExtraInfo and setting it to info object. If you observe ViewData object in Quick Watch window you will see this:

image

The ExtraInfo dynamic property is actually stored as ExtraInfo ViewData key ! Of course, you should stick to a uniform syntax while accessing this object (stick to ViewBag syntax in this case).

To retrieve CustomerInfo passed to the view you will write the following code:

<h3>@{
  @ViewBag.ExtraInfo.LastOrderID
  @: |  
  @ViewBag.ExtraInfo.LastOrderDate
  @: |  
  @ViewBag.ExtraInfo.LastOrderAmount
}</h3>

This time you don't need to typecast ExtraInfo to CustomerInfo and you can access its properties directly (Remember, however, that these properties won't be displayed in VS IntelliSense as such).

While deciding whether to use ViewData or ViewBag you need to consider developer choice and need for typecasting. Since ViewBag is just a wrapper over ViewData, whatever you store inside ViewBag is accessible only during the current request.

TempData 

TempData offers a dictionary storage like ViewData. However, values stored in TempData exists unless they are read in some view. Most commonly TempData is used to pass a value between the current request and the subsequent request. Consider an example where you are deleting a record from the database. You have created two action methods - Index() and Delete(). The Index() action method fetches a set of record and passes then to Index view to display in a table. The Index view consists of a table showing all the records and each table row has a Delete link that points to the Delete() action method. The Delete() action method does the job of deleting a record and once a record is deleted it takes the control to Index() action method so that the table can be re-displayed. Now suppose that when the table is re-displayed after deleting a record you wish to display a message to the user informing that so-and-so record has been deleted. You can't pass such a message using ViewData or ViewBag because here one request (the one that causes deletion) wants to pass data to another request (the one that renders a table of records).

The following code makes this clear:

public ActionResult Index()
{
  Customer model = new Customer() { ... };
  return View(model);
}

public ActionResult Delete(int id)
{
  //delete record here
  TempData["message"] = "Record deleted successfully!";

  //transfer control to Index(). This will be another request.
  return RedirectToAction("Index");
}

In this case request that invokes Delete() is the first request. During this request you store a TempData key named message and assign it some value. You then call RedirectToAction() to take the control to Index() method. Remember that RedirectToAction() returns HTTP 302 response to the browser so that the browser makes a GET request to the specified action method. At this point of time the second request is made. You wish to access data set during the first request in the second request and hence you use TempData.

The value set in the TempData can be accessed in the Index view like this:

<body>
    <h1>Index View</h1>
    <h3>@TempData["message"]</h3>
</body>

So far so good. While the above example works as expected, you need to be aware of a tricky thing. If you don't read the TempData values in the second request, they are persisted and can be read in yet subsequent request. They exist unless you read them in some view at which point of time they are removed. Consider the following code that makes this behavior clear:

public ActionResult Index1()
{
  TempData["message"] = "Test message!";
  return View();
}

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

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

Assume that Index1 view has a link pointing to Index2 action method and Index2 view has a link pointing to Index3 action method. Now, if Index1 and Index2 views do not use TempData at all then it is still available to Index3 view. However, if Index1 or Index2 views use TempData then it is removed as soon as the respective request completes. Index3 view won't be able to read TempData value in that case.

There is one more twist in the TempData story that you should be aware of. The TempData object has Keep() method that can preserve the value(s) even after they are read. To do so, once you finish reading the values call TempData.Keep() or TempData.Keep(<key>). Calling Keep() marks the tempdata key(s) for retention till next read operation.

It would be interesting to know that TempData values are actually stored in ASP.NET Session and are automatically removed once they are read. To prove that they are indeed stored in Session just disable session state altogether by adding this line in web.config:

<sessionState mode="Off"></sessionState>

If you try to run the same example now, you will get this error.

image

Just like ViewData, TempData also requires typecasting and null value checking while accessing its values.

To summarize,

  • ViewData, ViewBag and TempData allow you to pass values from a controller to a view.
  • ViewData and TempData objects allow you to store values as key-value pairs.
  • ViewBag object allows you to store values using object-properties syntax.
  • ViewBag is a wrapper over ViewData.
  • ViewData and ViewBag values are available only during current request.
  • TempData values are accessible in the current request and the subsequent requests. They are removed when a view reads them.
  • TempData internally uses Session to store its values. The values are removed once they are read.
  • ViewData and TempData require typecasting and null checking whereas ViewBag doesn't need such checking.
posted Oct 6, 2016 by Shivaranjini

  Promote This Article
Facebook Share Button Twitter Share Button LinkedIn Share Button

...