ASP.NET MVC Views: Passing Data to Views

September 15th, 2014

ASP.NET MVC has a number of ways to pass data back to Views. Find out the three most common ways of passing data and the recommended way.

If you've worked with ASP.NET WebForms and recently started ASP.NET MVC, you've no doubt already experienced the elimination of the IsPostBack. Session is still available, but to be honest, I have never used since I switched to MVC.

This change in thinking leaves most newbies to MVC wondering how can you send data to the Views. With MVC, things are a little different. In the controller, you retrieve the data specific to that page and pass that data to your View (html page).

But how do you pass the data back to the View?

Let's check some examples.

(NOTE: The examples below are using the Razor syntax. While you can use the WebForm approach, the Razor syntax is recommended).

ViewData

The simplest way to send data to the view is through the ViewData property. Defined as a Dictionary object in the Controller class, you can populate this with any data type, but there are drawbacks to this approach.

When the view displays the data, it needs to know what type it's using. ViewData is defined as a Dictionary object. Plain and simple. If you pass in a List<Customer>, you will need to cast it as a List when you iterate through it.

Kind of cumbersome. Take the following example:

Controller

using System.Collections.Generic;
using System.Web.Mvc;

namespace ThinController.Controllers {     public class FaqController : Controller     {         public ActionResult Index()         {             var customer = "Jose";             var account = 1;
            ViewData["Message"] = string.Format("Customer: {0} ({1})", customer, account);
            var customers = new List<string>         {              "John",              "Bob",              "Fred"          };
ViewData["Customers"] = customers;
            return View();         }     } }

View

<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
</head>
<body>
@@ViewData["Message"]
<hr />
<ul>
    @@foreach (var customer in (ViewData["Customers"] as List<string>))
    {
        <li>@@customer</li>
    }
</ul>
</body>
</html>

Maybe it's just me, but I have issues with the List<string> in the middle of a foreach..loop.

ViewBag

The ViewBag is a step up from the ViewData property. The ViewBag is strictly a wrapper around the ViewData to make the object type dynamic. This does make your HTML a little cleaner, but still requires the casting of a type.

Here's another example:

Controller

using System.Collections.Generic;
using System.Web.Mvc;

namespace ThinController.Controllers {     public class FaqController : Controller     {         public ActionResult Index()         {             var customer = "Jose";             var account = 1;

            ViewBag.Message = string.Format("Customer: {0} ({1})", customer, account);

            var customers = new List<string>         {              "John",              "Bob",              "Fred"          };
            ViewBag.Customers = customers;
            return View();         }     } }

View

<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
</head>
<body>
    @@ViewBag.Message
    <hr />
    <ul>
        @@foreach (var customer in ViewBag.Customers)
        {
            <li>@@customer</li>
 
        }
    </ul>
</body>
</html>

However, if you're using a non-native object, like a FAQ or Customer object that has multiple properties, you need to explicitly identify that class.

NOTE: If you create an anonymous type and pass it through to the ViewBag, it will not work!

The Razor Engine needs to know the type of object so it can display the data in the View.

Controller

using System.Web.Mvc;
using ThinController.Repository;

namespace ThinController.Controllers {     public class FaqController : Controller     {         public ActionResult Index()         {             var customer = "Jose";             var account = 1;
            ViewBag.Message = string.Format("Customer: {0} ({1})", customer, account);
            var repository = new FaqRepository();             var faqs = repository.GetAll();
            ViewBag.FAQList = faqs;
            return View();         }     } }

FaqRepository

using System.Collections.Generic;
using ThinController.Models;
namespace ThinController.Repository
{
    public class FaqRepository
    {
        public IEnumerable<Faq> GetAll()
        {
            return new List<Faq>
            {
                new Faq()
                {
                    Question = "So How long has the site been up?",
                    Answer = "A short time."
                },
                new Faq
                {
                    Question = "How big is your audience?",
                    Answer = "Just two, my wife and my mother."
                },
                new Faq
                {
                    Question = "How long have you been programming?",
                    Answer = "3 decades."
                }
            };
        }
    }
}

View

<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
</head>
<body>
    @@ViewBag.Message
    <hr />
    <ul>
        @@foreach (Faq faq in ViewBag.FAQList)
        {
            <li><span>@@faq.Question</span> &mdash; @@faq.Answer</li>
        }
    </ul>
</body>
</html>

TempData

TempData is a one-shot deal. When you are in the Controller performing an HttpPost, you may need to set something to display a message to the user saying everything processed properly or an error occurred. This is where the TempData property is useful.

Controller

using System.Web.Mvc;
using ThinController.Repository;
namespace ThinController.Controllers
{
    public class FaqController : Controller
    {
        public ActionResult Index()
        {
            var customer = "Jose";
            var account = 1;
            if (TempData["Message"] != null)
            {
                ViewBag.Message = TempData["Message"].ToString();
            }
            else
            {
                ViewBag.Message = string.Format("Customer: {0} ({1})", customer, account);
            }
            var repository = new FaqRepository();
            var faqs = repository.GetAll();

            ViewBag.FAQList = faqs;
            return View();         }

        [HttpPost]         public ActionResult Index(FormCollection forms)         {             if (forms["name"] != "John")             {                 TempData["Message"] = "This data is only for John. Access Denied.";             }

            return RedirectToAction("Index");         }     } }

ViewModel

The most robust and easiest way to pass data to a View is through using a custom POCO object called a ViewModel. A ViewModel can be as simple as a native type (string, int, datetime, etc.) or can be as complex as a custom object with numerous properties. The benefit of the ViewModel method is that your View will be able to read and display it.

In my opinion, this is the recommended way of passing data to the Views.

Once again, see the example below.

Controller

using System.Web.Mvc;
using ThinController.Models;
using ThinController.Repository;
namespace ThinController.Controllers
{
    public class FaqController : Controller
    {
        public ActionResult Index()
        {
            var repository = new FaqRepository();
            var model = new FaqViewModel
            {
                FaqList = repository.GetAll(),
                MetaDescription = "This is a test for the Thin Controller",
                MetaKeywords = "ASP.NET MVC ThinController, ASP.NET MVC Controller",
                PageTitle = "FAQ List | TestApp"
            };

            return View(model);         }     } }

View

@@model ThinController.Models.FaqViewModel
<!DOCTYPE html>
<html>
<head>
    <title>@@Model.PageTitle</title>
    <meta name="description" content="@@Model.MetaDescription" />
    <meta name="keywords" content="@@Model.MetaKeywords" />
</head>
<body>
    <ul>
        @@foreach (var faq in Model.FaqList)
        {
            <li><span>@@faq.Question</span> &mdash; @@faq.Answer</li>
        }
    </ul>
</body>
</html>

Model

namespace ThinController.Models
{
    public class Faq
    {
        public string Question { get; set; }
        public string Answer { get; set; }
    }
}

Again, your ViewModel object can hold any type of object you want to pass into the View.

If you think about it, your custom ViewModel takes care of a number of issues when you use this approach.

  1. It allows you to create a base model for all of your pages which is extremely helpful (i.e. Create a BaseViewModel that has PageTitle, Message, MetaKeywords, and MetaDescription and build off of that).
  2. It provides a cleaner HTML view than casting all over the place.
  3. If, by any chance, that the ViewBag or ViewData is removed from MVC, you'll still have your custom object that passes data back to the view.

What about Session or ViewState?

Personally, I've never been a fan of ViewState. It just never felt right to me when .NET WebForms came out. In the ASP.NET MVC world, it takes a different approach when passing data back and forth. I have NEVER used Sessions in my ASP.NET MVC code.

I will even go as far as to say that I don't even like Sessions. Sessions break applications way too easily.

I'm just glad there are other people who feel the same way as I do.

Conclusion

When starting ASP.NET MVC, there is a gradual learning curve when passing data to your Views. Once you're over that hurdle, you will start to see an eye-opening way of developing granular web pages for your audience.

If I've missed any other method of passing data back to ASP.NET MVC views, post your opinion in the comments below.