ViewBag and how it works with ViewData

I previously posted this quite some time ago on our internal company blog but someone asked me about it the other day so I thought I'd re-post it here for anyone that's interested.

I had a brief discussion with a member of the team the other day on the new ViewBag dynamic in MVC 3 which acts as a wrapper for the previous ViewData MVC dictionary.  The basic idea is that instead of writing ViewData["SomeData"] you are able to write ViewBag.SomeData replacing magic strings with magic properties.  They were wondering how the underlying framework wrapped this dictionary and it sparked my interest so here's how it works and how I went about investigating it.

I started off by pulling down the source code for MVC 3 from  http://aspnet.codeplex.com/releases/view/58781.  After a bit of digging around in the code (not a lot I might add) I came across the class DynamicViewDataDictionary and it's corresponding test class.  Simply by setting a break point in one of the tests it was easy enough to see that when a property was accessed in the dynamic object it corresponded to a call to the TryGetMember method.

public override bool TryGetMember(GetMemberBinder binder, out object result) {
    result = ViewData[binder.Name];
    // since ViewDataDictionary always returns a result even if the key does not exist, always return true
    return true;
}

This uses the private property ViewData in order to retrieve the ViewDataDictionary using a delegate.  Interestingly this uses the .NET 3.5 System.Func<TResult>.  As an aside this presents a rather nice way of representing a method that can be passed as a parameter without explicitly declaring a custom delegate.  Borrowing from the MSDN documentation it can best be expressed using the following two code samples :

delegate bool WriteMethod();

public void DoSomething()
{
    WriteMethod methodCall = SendToFile;
    methodCall();
}

private bool SendToFile()
{
    //elided
}

This can just be replaced with the following :

public void DoSomething()
{
    Func<bool> methodCall = SendToFile;
    methodCall();
}

private bool SendToFile()
{
    //elided
}

There are also many overloads for this which allows the you to use this method to replace delegates with a number of different input parameters.

Returning to the original question; the reason that we are able to replace ViewData["SomeData"] with ViewBag.SomeData is that the ViewDataDictionary is derived from the DynamicObject class which was introduced in .NET 4.0.  This class provides a base class for specifying dynamic behavior at run time.  The MSDN documentation provides us with an explanation of how the the ViewDataDictionary by overriding certain members of the DynamicObject class can allow us to use magic properties as opposed to magic strings:

The DynamicObject class enables you to define which operations can be performed on dynamic objects and how to perform those operations. For example, you can define what happens when you try to get or set an object property, call a method, or perform standard mathematical operations such as addition and multiplication. This class can be useful if you want to create a more convenient protocol for a library. For example, if users of your library have to use syntax like Scriptobj.SetProperty("Count", 1) , you can provide the ability to use much simpler syntax, like scriptobj.Count = 1. You cannot directly create an instance of the DynamicObject class. To implement the dynamic behavior, you may want to inherit from the DynamicObject class and override necessary methods. For example, if you need only operations for setting and getting properties, you can override just the TrySetMember and TryGetMember methods.

There we have it simply by implementing the DynamicObject class we are able to access the ViewBag.SomeData or ViewData["SomeData"] interchangeably.  Obviously we should all be using strongly typed view models anyway but it expresses an interesting new feature of the .NET 4 framework all the same and also highlights how useful unit tests can be to anyone looking into a codebase for the first time.

NB. In C#, to enable dynamic behavior for instances of classes derived from theDynamicObject class, you must use the dynamic keyword

For those wondering how we end up calling these methods the answers lie in the dynamic language runtime.  When you make a call to obj.SampleProperty in an instance of the SampleObject class the DLR will use a language binder to look for the static definitition of this property in the SampleObject class.  If there is no such property the DLR calls the TryGetMember method passing in the property name as the binder.