ASP.NET MVC provides us with three ways to pass data from Controllers to Views. We've already discussed using TempData to create a FlashMessage partial:
Now, let's discuss the second method available to us: ViewBag.
What's in the ViewBag?
ViewBag, according to MSDN,
"enables you to dynamically share values from the controller to the view".
The key word there is "dynamically"; ViewBag is of type dynamic
. This is a special type in C#; instances of dynamic
are a type where the actual type (e.g. int, string, char, DateTime, etc) isn't determined until runtime. Which means we can do things like this:
public ActionResult Index() //We'll set the ViewBag values in this action
{
ViewBag.PageTitle = "This is the page title";
ViewBag.PageDescription
= "This is the page description. We'll make it rather longer.";
ViewBag.PageCreateDate = DateTime.Now;
ViewBag.CurrentUser = new User()
{
Name = "Test Name",
ID = 4,
LogonDate = DateTime.Now.AddDays(-20)
};
return View();
}
(Scott Hanselman has a pretty good post about dynamic types over on his blog.)
We can put anything we want in the ViewBag
. It doesn't matter what it is, because it won't be resolved until runtime. Then, we can use it on our views like this:
<h3>@ViewBag.PageTitle</h3>
<p>
@ViewBag.PageDescription
</p>
<span>Created on @ViewBag.PageCreateDate.ToShortDateString()</span>
Current user:
<div>
<dl>
<dt>Name:</dt>
<dd>@ViewBag.CurrentUser.Name</dd>
<dt>ID:</dt>
<dd>@ViewBag.CurrentUser.ID</dd>
<dt>Logon Date:</dt>
<dd>@ViewBag.CurrentUser.LogonDate.ToShortDateString()</dd>
</dl>
</div>
All in all a pretty sweet thing! We can take anything we want, shove it in ViewBag
, and then use it on the View. What could be better?
The Hole in the Bag
As it turns out, almost anything is better than using ViewBag
. The problem with ViewBag is exactly because it is of type dynamic
. That means, since you can type anything you want as a value in it, you won't catch errors like this:
public ActionResult Index() //We'll set the ViewBag values in this action
{
ViewBag.PageTitle = "This is the page title";
ViewBag.PageDesciption
= "This is the page description. We'll make it rather longer.";
ViewBag.PageCreateDate = DateTime.Now;
ViewBag.CurrentUser = new User()
{
Name = "Test Name",
ID = 4,
LogonDate = DateTime.Now.AddDays(-20)
};
return View();
}
Did you catch what the problem is? Because the compiler won't catch this error. The property PageDesciption
is misspelled.
See, dynamic
types are exempt from normal type checking, which means things like checking to make sure a name exists in the ViewBag
will not occur at all. That PageDesciption
is now a variable name in ViewBag
, and the view won't find the PageDescription
it is looking for; consequently it will throw an exception at runtime.
But we can also attempt to do some crazy things by treating one type like another type. Say we wanted to treat a DateTime
like it's a string
:
public ActionResult Index() //We'll set the ViewBag values in this action
{
ViewBag.PageTitle = "This is the page title";
ViewBag.PageDescription
= "This is the page description. We'll make it rather longer.";
ViewBag.PageCreateDate = DateTime.Now;
ViewBag.LastIndexOfC = ViewBag.PageCreateDate.LastIndexOf('c');
ViewBag.CurrentUser = new User()
{
Name = "Test Name",
ID = 4,
LogonDate = DateTime.Now.AddDays(-20)
};
return View();
}
The property LastIndexOfC
is checking the value of PageCreateDate
and attempting to get the last index of 'c', because it is assuming that PageCreateDate
is of type string
. But it's not a string
, it's of type DateTime
, and we won't catch this error until we attempt to execute the code, when we will get an exception. There's no protection from misspelling, or from using one type as another, so we can pretty easily shoot ourselves in the foot.
Toss the Bag!
So what's our alternative? Using a view model of course!
[HttpGet]
public ActionResult About() //This time, let's use a ViewModel
{
HomeAboutVM model = new HomeAboutVM();
model.PageTitle = "This is the About page";
model.PageDescription = "See how much better using a ViewModel is?";
model.PageCreateDate = DateTime.Now;
model.CurrentUser = new User()
{
Name = "Test Name",
ID = 4,
LogonDate = DateTime.Now.AddDays(-20)
};
return View(model);
}
Now, we've got strong type protection, and any attempts to treat a variable of one type like a different type will be caught and handled by the compiler.
We should always favor ViewModels over the ViewBag
, if for no other reason than ViewModels are strongly typed.
One Exception
There's one scenario in which using ViewBag
is appropriate (because it's mandatory), and that's when we want to set a Page Title on a given view:
@{
ViewBag.Title = "This shows up in the browser tab";
}
Other than that, we should avoid using ViewBag
. It's a poor way to transfer data between controllers and views.
Feel free to grab the sample project demonstrating ViewBag
from GitHub.
Happy Coding!