Hate the ASP.NET MVC ViewBag as much as I do? Here's how to remove it!

Let's kick things off with a rant (sorry!)

I'm really not a fan of the ASP.NET MVC ViewBag, or the ViewData dictionary for that matter. I have found that code which uses both of these can be difficult to maintain as essentially they aren't strongly typed and don't have IntelliSense. While I agree that they might be quick and easy to use for small personal projects, proofs of concept, or just to quickly get the ball rolling with a new application, I really don't like using them for mature projects, especially when collaborating with other developers.

The ViewBag was introduced in MVC 3 and serves no purpose other than as a wrapper for the ViewData dictionary. If you're uncertain about why the ViewBag might not be a good choice to use, this blog post sums everything up quite nicely.

I personally agree with never using the ViewBag and only using strongly-typed view models and I firmly believe that the two shouldn't be mixed. I therefore go out of my way to refactor all usages of the ViewBag out of my projects.

I feel that using the ViewBag is encouraged a little too much by Microsoft. If you create a brand new ASP.NET MVC project, you'll find default code like this:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";
        return View();
    }

    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";
        return View();
    }
}

I'm not saying that Microsoft should provide a sample project which provides a complicated view model structure. It bothers me though that providing code like this basically says to a lot of newcomers to MVC "This is the best way of doing things". It also bothers me that if I don't want to use the ViewBag, whenever I create a new MVC project I have to refactor everything so it uses models instead and this can take some time. Do a solution-wide search for "ViewBag" in your new project and Visual Studio will find:

ViewBag occurrences

That's a lot of refactoring to do!

I take the view that if you want to use the ViewBag instead of view models (or someone else on your team wants to use it) then fair enough, that's completely your decision. I would however suggest implementing a solution for making it strongly-typed, an example of which I will provide in another post soon.

I on the other hand hate the ViewBag so much that I decided to remove it from my own projects.

How to remove the ViewBag from your controller classes

The ViewBag is usually accessible via the ControllerBase.ViewBag Property. The goal is to prevent this property from being used and ideally to throw a compile-time error if a pesky rogue developer comes along and tries to use it anyway.

The solution is to create an abstract BaseController class, and have your MVC controllers inherit from it. We can then modify the behaviour of the property.

public abstract class BaseController : Controller
{
    [Obsolete("If you use the ViewBag, you deserve to be stabbed.", true)]
    public new object ViewBag { get; set; }
}

So what's happening here?

  • BaseController hides the ViewBag property of Controller with its own property of the same name.
  • Obsolete marks this property as, well, obsolete!

The ObsoleteAttribute class can be used for marking elements of your program which are no longer in use. The particular constructor I'm using above allows you to provide string and boolean arguments. The string allows you to specify the message displayed to a developer when they use the ViewBag property in your code. If the boolean you pass in is true, usage of the ViewBag property will generate a compiler error. If it's false, it will generate a compiler warning instead.

ViewBag compile-time error in controller

ViewBag compile-time error in Error List

How to remove the ViewBag from your Razor views

You may have noticed already but you're still able to use the ViewBag from within your Razor views. We'll have to remove it from them too!

My solution is to create a subclass of WebViewPage and hide the ViewBag property, in the same way that we did for the BaseController class.

public abstract class BaseWebViewPage<T>; : WebViewPage<T>
{
    [Obsolete("Thought you could get away with using this in a Razor view? Nice try, sucker :)", true)]
    public new dynamic ViewBag { get; set; }
}

public abstract class BaseWebViewPage : BaseWebViewPage<dynamic> { }

What does this mean?

  • We're creating a generic abstract subclass of WebViewPage called BaseWebViewPage. Our Razor views will use this class instead of the default WebViewPage class. If you're wondering why this class should be abstract, there's a great explanation here.
  • BaseWebViewPage hides the ViewBag property of WebViewPage with its own property of the same name.
  • We use the Obsolete attribute as we did with the BaseController class.
  • The non-generic version of the BaseWebViewPage class is actually there for a trivial reason. Without it, intellisense will complain about the changes we're about to make to the Web.config file.

We'll now need to make a change to the Web.config file located in the Views folder of your project (not the Web.config file located in the root of your project). Open this file and you should find a section which looks like the following (with some small project-specific differences):

<system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="System.Web.Mvc"/>
        <add namespace="System.Web.Mvc.Ajax"/>
        <add namespace="System.Web.Mvc.Html"/>
        <add namespace="System.Web.Optimization"/>
        <add namespace="System.Web.Routing"/>
        <add namespace="MyExampleProject"/>
      </namespaces>
    </pages>
</system.web.webPages.razor>

Replace this line:

<pages pageBaseType="System.Web.Mvc.WebViewPage">

With this line (replacing my namespace with yours):

<pages pageBaseType="Mvc5NoViewBag.Models.BaseWebViewPage">

And that's it!

If the ViewBag is now used within a Razor view, you'll see compilation errors such as these:

ViewBag error in Razor view

ViewBag error in Error List

And if you attempt to access these pages regardless of these errors, you'll receive a lovely runtime compilation error:

ViewBag Razor runtime compilation error

I've done all this and now I have tons of errors in my project! What now?

You've implemented changes which will now cause usages of the ViewBag to throw errors, however you still need to go through your project and actually remove those usages. This is somewhat easier to do now though, as all you have to do is go through the list of errors in the Visual Studio Error List and fix/refactor your code accordingly.

If you're starting a brand new project, you can just clone/download my example MVC5 project from GitHub instead, where I've done all this for you.

Certain ViewBag values are accessed within the _Layout.cshtml file and individual Razor views for each page. My solution for replacing these ViewBag usages was to use a base view model pattern. In the future I'll write a detailed explanation about how this works and why I use it. In the meantime, please feel free to check out my sample project.

A brief explanation of the Obsolete attribute

This attribute would normally be used for less "stabby-stab-stab-developer-murder" situations and more for general deprecation of parts of a program. For example, you might develop an API which is used by other developers and in a future update, you may want to rename or migrate a particular class or method.

This would be a problem as it would cause consumers of your API to have broken code and they would have no idea why, leaving them to do some research to determine an appropriate workaround. By using the Obsolete attribute, you can both prevent the old system being used and advise your users on an appropriate workaround. You can either trigger a warning and say "Your code is fine for now but in the future you'll have to use [insert name of other class/method here]" or you can throw a compilation error and say "Sorry, this was deprecated, please use [insert name of other class/method here]"

Wait, so does that mean we're using this attribute incorrectly?

Sort of, I mean I wouldn't go as far as saying that using this attribute for this reason is incorrect or inherently wrong, but I would go as far as agreeing that this was probably not what Microsoft intended it to be used for when they added it to their API.

In my defence, my original plan was to create my own attribute which is capable of throwing a compilation error. Unfortunately, this isn't possible and essentially the ObsoleteAttribute class does this "magically". My next idea was to create a subclass of ObsoleteAttribute and name it something like StabbyStabbyCodeReviewAttribute, however this was also not possible as ObsoleteAttribute is a sealed class. I was a little bit annoyed about this at first but in retrospect it makes sense for Microsoft to lock down this compiler magic.

Basically, I have no other choice but to use this magic attribute. I've essentially solved my problem of the ViewBag being used "incorrectly", by using an attribute "incorrectly".

Seems a little ironic!



David Omid

Hi, my name's David and I'm a senior software engineer from London.