Anonymous View Models in ASP.NET MVC Using Dynamics

The introduction of the dynamic keyword in C# 4.0 allows you to do a lot of things you couldn’t do otherwise, but it also makes it easy to forget that C# is still not a dynamic language, so there are limitations. Recently I found myself wanting to whip up a quick test page for something, with a very simple controller action and view. It wasn’t meant to be permanent, so creating a dedicated view model class in the project seemed like overkill. I figured I could just return an anonymous object as the model, and use a dynamic view to display it. I was wrong.

The Limits of Anonymous Classes

Let’s take a look at a quick example to demonstrate the problem. Here is my very simple controller action, that uses an anonymous class as the view model:

public ActionResult UsingDynamic()
{
  return View(new
  {
    TestString = "This is a test string"
  });
}

Now let’s make sure the data gets sent correctly to the view. First we’ll try just printing the model to the page using its ToString() method:

<%: Model %>

We get the output:

{ TestString = This is a test string }

So far so good! We see an object that has the TestString property and value we wanted. Now let’s try printing out the value of TestString:

<%: Model.TestString %>

This time ASP.NET throws an exception back at us, saying “‘object’ does not contain a definition for ‘TestString’”. How can this be? When we printed the object we could clearly see it had a property named TestString, and had a value for it. The answer here lies in the fact that anonymous classes are internal by default, meaning they are only accessible to the assembly in which they were created. Once you cross that assembly boundary, it gets treated as a regular object, which does not have the TestString property.

So if anonymous classes are internal, how can we accomplish what we want? There are a few ways I could think of.

Use a POCO

I know I said this was the solution I was trying to avoid, but I thought it was worth mentioning because in a majority of cases, having a well defined view model is a good choice. To wire this up using a defined view model, it would look something like this:

public class PocoViewModel
{
    public string TestString { get; set; }
}

And the controller action:

public ActionResult UsingPoco()
{
    return View(new PocoViewModel()
    {
        TestString = "This is a test string"
    });
}

The view would just need to inherit from ViewPage and it would work as expected.

ExpandoObjects

Another new addition in .NET 4.0 is the ExpandoObject. An ExpandoObject is an object that allows for dynamically adding and removing members at runtime. Since that is its purpose, it gets us something pretty similar to what we were trying to achieve with the anonymous class, but it will make it over the assembly boundary. The controller action using an ExpandoObject would look like:

public ActionResult UsingExpando()
{
    dynamic viewModel = new ExpandoObject();
    viewModel.TestString = "This is a test string";

    return View(viewModel);
}

The view should inherit from ViewPage (which is now actually the default), and it would be able to successfully access the TestString property. This is actually pretty much what they’re doing in ASP.NET MVC 3 with the new ViewModel property in Controller.

Dynamic Reflection

But what if you don’t want to use an ExpandoObject, and just want to throw an anonymous class at your view and have it just work? You can actually make this happen with an impressively small amount of code. First, we’ll create a custom dynamic class that wraps another object instance, and uses reflection to provide property access to that instance. To do that, the class will extend the DynamicObject class, which handles most of the heavy lifting for dealing with dynamics. If we wanted to do everything outselves, we could implement IDynamicMetaObjectProvider ourselves (which DynamicObject does), but in this case there’s no real reason to, so why not let DynamicObject do the hard work for us?

public class DynamicPageObject : DynamicObject
{
    private object _originalObject;

    public DynamicPageObject(object originalObject)
    {
        _originalObject = originalObject;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = _originalObject
                    .GetType()
                    .InvokeMember(binder.Name,
                                  BindingFlags.GetProperty
                                      | BindingFlags.Instance
                                      | BindingFlags.Public,
                                  null,
                                  _originalObject,
                                  null);

        return true;
    }
}

There we override the TryGetMember(…) function of DynamicObject, to allow it to use reflection over the original object to provide its member access. It’s pretty simple reflection, and that’s all there is to it.

Next we’ll create our own class that extends ViewPage that will automatically wrap the model in a DynamicPageObject:

public class DynamicPage : ViewPage
{
    public new dynamic Model { get; set; }

    protected override void SetViewData(ViewDataDictionary viewData)
    {
        base.SetViewData(viewData);

        Model = new DynamicPageObject(viewData.Model);
    }
}

We override the base Model property to use a dynamic model, which gets set to a DynamicPageObject that wraps around the original model. That’s all the code we need to make this work, so let’s give it a shot. The controller action is identical to the one from the original example, using an anonymous class:

public ActionResult UsingCustomDynamic()
{
    return View(new
    {
        TestString = "This is a test string"
    });
}

For the view, the only thing we need to do is have it inherit from DynamicPage and we’re done. Now we have a controller passing in an anonymous class as a view model, and a view that can handle that dynamically.

comments powered by Disqus
Navigation