Welcome to MSDN Blogs Sign in | Join | Help

Passing anonymous objects to MVC views and accessing them using dynamic

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.

Published Friday, December 18, 2009 4:06 PM by davidebb
Filed under: , ,

Attachment(s): MvcDynamicViewPage.zip

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@ali: sorry, this requires VS 2010 because it relies on the new C# dynamic feature which didn't exist before.  Note that you can download VS 2010 Beta 2 for free if you'd like to try it.

Saturday, December 19, 2009 11:47 AM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

nop my bad to missed the dynamic keyword.

thanks again for the great post .

Sunday, December 20, 2009 3:13 AM by ali

# re: Passing anonymous objects to MVC views and accessing them using dynamic

That is really cool. They should replace ViewData with this little gem. Great Job.

Sunday, December 20, 2009 12:32 PM by Khalid Abuhakmeh

# re: Passing anonymous objects to MVC views and accessing them using dynamic

I agree with Khalid this would be awesome as the default.

Sunday, December 20, 2009 12:47 PM by Rangoric

# re: Passing anonymous objects to MVC views and accessing them using dynamic

so can I apply this "dynamic goodness" to my MVVM structure in Silverlight is some way?

Sunday, December 20, 2009 3:03 PM by adefwebserver

# re: Passing anonymous objects to MVC views and accessing them using dynamic

Very cool.

Instead of letting it throw an exception, I capture it and then display all the available properties in the exception message

or....

Depending on configuration I suppress the error and return empty string. (eg. for TempData)

Sunday, December 20, 2009 3:08 PM by Adam Schröder

# re: Passing anonymous objects to MVC views and accessing them using dynamic

Because this uses Reflection, is this going to result in a slower databinding?  This is certainly convenient compared to using ViewData and it's inherit danger of typing the keys in incorrectly, but I worry that using reflection will result in performance degradation?

Sunday, December 20, 2009 9:51 PM by Sharbel

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@adefwebserver: I'm less familiar with Silverlight scenarios, but this technique is not specific to MVC, so I suspect that there are good ways to make use of C# 4.0 dynamic there as well.

Sunday, December 20, 2009 11:20 PM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@Adam: yes, these are good suggestions. It probably makes sense to make the default behavior return empty string instead of throwing. Otherwise, dealing with optional data becomes tricky.

Sunday, December 20, 2009 11:22 PM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@Sharbel: I haven't done any perf measurement, so I don't have hard numbers.  The reflection call will indeed be slower that the dictionary lookup.  However, it's not clear whether this will result in measurable perf degradation of the overall request.  I would be interesting to try measuring it.

Sunday, December 20, 2009 11:26 PM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

Couldn't you just InternalsVisibleTo the MVC dll?  You'd have to sign your assembly, of course...

Monday, December 21, 2009 5:48 AM by WillSullivan

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@WillSullivan: the difficult part is that the views are compiled on the fly, and you cannot predict what assembly they will be in. This makes it hard to put an InternalVisibleTo attribute on the main assembly.

As an aside, I haven't verified that this would allow the default binder to work, though it certainly would make sense.

Monday, December 21, 2009 11:26 AM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

A slight idea, but it may result in exception after deploy

Tuesday, December 22, 2009 12:35 AM by Jack

# re: Passing anonymous objects to MVC views and accessing them using dynamic

Another option here is to use the ExpandoObject, for example,

your page would inherit;

System.Web.Mvc.ViewPage<dynamic>

and in your controller;

dynamic model = new ExpandoObject();

model.Message = "Hello World";

return View(model);

The downside to the ExpandoObject is that it cant except properties during initialzation.

Tuesday, December 22, 2009 4:32 AM by Cain O'Sullivan

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@Jack: I'm not sure what you mean by 'exception after deploy'.  Can you clarify?

Tuesday, December 22, 2009 9:52 AM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@Cain: yes, ExpandoObject is sort of the 'standard' approach, and it is certainly an option.  The main point of this post is that I really wanted to find a way to pass in regular C# anonymous objects, which doesn't work without extra effort.

Tuesday, December 22, 2009 9:54 AM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

Umm just use  Inherits="System.Web.Mvc.ViewPage<object>" and use the templating in MVC2.

Also when using dynamic you cannot make an instance. EX. dynamic d = new dynamic(); \\ is incorrect. Instead you have to use

dynamic d = new Object();

d.Name = "";

Tuesday, December 22, 2009 2:12 PM by Charlie

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@Charlie: I don't see how MVC2 templating will help with dealing with anonymous objects.

Also, you won't be able to use dynamic as you describe, since you can't add properties to Object.  Instead, look at ExpandoObject if you want to do this.

Tuesday, December 22, 2009 4:04 PM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

Yes you can add Properties to an Object as long as you declare it initially with dynamic.

Go try it, I use it all the time.

Wednesday, December 23, 2009 11:12 AM by Charlie

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@Charlie: I just tried the exact two lines you have above in a console app, and it blew up with: "'object' does not contain a definition for 'Name'".  Are you doing anything differently?

Wednesday, December 23, 2009 11:24 AM by davidebb

# re: Passing anonymous objects to MVC views and accessing them using dynamic

@davidebb Im Installing Beta2 on my work machine, get back to you soon.

Wednesday, December 23, 2009 11:33 AM by Charlie

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker