Passing anonymous objects to MVC views and accessing them using dynamic

Note (12/22/2011): now that MVC 3 has direct support for dynamic, the technique below is no longer necessary. This post is in fact what led to integrating the feature into MVC!

First, I’ll start with a little disclaimer: this post is not about whether using dynamic is better/worse than static typing.  Instead, it’s about making it more convenient to use dynamic if you choose to go that route.  Clearly, some people dislike dynamic, as you can see in the comments in that post from Phil Haack, and for the most part, all the key arguments for/against have been made.

So anyway, let’s proceed… Recently, a few people have experimented with extending their view pages from ViewPage<dynamic>.  The idea is to then be able to access model data using the more convenient dynamic syntax.  e.g. check out this thread on StackOverflow, as well as Phil’s post I mention above.

One limitation that people are hitting is that you can’t easily pass an anonymous object as your model, because anonymous types are internal.

What we’re trying to write

e.g. Let’s say we’re trying to write this code in the controller:

 public class HomeController : Controller {
    public ActionResult Index() {
        return View(
            new {
                Message = "Welcome to ASP.NET MVC!",
                Date = DateTime.Now
            });
    }
}

Note that we’re passing an anonymous object as the model.  The main reason to do this is to avoid the need to create an explicit ViewModel type.  Obviously, that’s controversial, but it should be viewed more as a simpler alternative to writing

 ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["Date"] = DateTime.Now;
return View();

And then you’d change your view to have Inherits="System.Web.Mvc.ViewPage<dynamic>", which ideally would let you write something like this:

 <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%: Model.Message %></h2>
    <p>
        The date is <%: Model.Date %>
    </p>
</asp:Content>

 

Which is simpler than having to dig things out of the view data dictionary through string indexing.

But the default dynamic binder won’t let us!

Unfortunately, if you try to run this code, it’ll blow up with this error: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: '<>f__AnonymousType1<string,System.DateTime>.Message' is inaccessible due to its protection level

The reason for this is that the anonymous type being passed in the controller in internal, so it can only be accessed from within the assembly in which it’s declared.  Since views get compiled separately, the dynamic binder complains that it can’t go over that assembly boundary.

But if you think about it, this restriction from the dynamic binder is actually quite artificial, because if you use private reflection, nothing is stopping you from accessing those internal members (yes, it even work in Medium trust).  So the default dynamic binder is going out of its way to enforce C# compilation rules (where you can’t access internal members), instead of letting you do what the CLR runtime allows.

Solution: write our own!

I’m not sure why it was designed this way, but the good news is that it’s easy to work around by writing our own custom DynamicObject which binds through private reflection!  I’ve written a couple posts that make use of custom DynamicObjects (find them here), and the basic concept is the same: given the name of a property, write whatever code you want to produce its value.

Here, we’re not only going to write a custom Dynamic Object, but also a custom DynamicViewPage that uses it.  And doing all this takes surprisingly little code.  Here is the full implementation:

 public class DynamicViewPage : ViewPage {
    // Hide the base Model property and replace it with a dynamic one
    public new dynamic Model { get; private set; }

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

        // Create a dynamic object that can do private reflection over the model object
        Model = new ReflectionDynamicObject() { RealObject = ViewData.Model };
    }

    class ReflectionDynamicObject : DynamicObject {
        internal object RealObject { get; set; }

        public override bool TryGetMember(GetMemberBinder binder, out object result) {
            // Get the property value
            result = RealObject.GetType().InvokeMember(
                binder.Name,
                BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                null,
                RealObject,
                null);

            // Always return true, since InvokeMember would have thrown if something went wrong
            return true;
        }
    }
}

As you can see there is not that much to it: when we need to look up a property value, we use private reflection and that’s the end of it.

And now it all works!

Once we have this, all that’s left to do is use it as our base class for the view, e.g.

 <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="MvcHelpers.DynamicViewPage" %>

And we’re then able to write exactly what we wanted above in the controller and the view, without hitting any access issues.

The full VS2010 project is attached to this post.

MvcDynamicViewPage.zip