Fun With Method Missing and C# 4

archived, asp.net, code, asp.net mvc comments edit

UPDATE: Looks like the CLR already has something similar to what I did here. Meet the latest class with a superhero sounding name, ExpandoObject

**Warning: What I’m about to show you is quite possibly an abuse of the C# language. Then again, maybe it’s not. ;) You’ve been warned.

Ruby has a neat feature that allows you to hook into method calls for which the method is not defined. In such cases, Ruby will call a method on your class named method_missing. I showed an example of this using IronRuby a while back when I wrote about monkey patching CLR objects.

Typically, this sort of wild chicanery is safely contained within the world of those wild and crazy dynamic language aficionados, far away from the peaceful waters of those who prefer statically typed languages.

Until now suckas! (cue heart pounding rock music with a fast beat)

C# 4 introduces the new dynamic keyword which adds dynamic capabilities to the once staid and statically typed language. Don’t be afraid, nobody is going to force you to use this (except maybe me). In fact, I believe the original purpose of this feature is to make COM interoperability much easier. But phooey on the intention of this feature, I want to have some fun!

I figured I’d try and implement something similar to method_missing.

The first toy I wrote is a simple dynamic dictionary which uses property accessors as the means of adding and retrieving values from the dictionary by using the property name as the key. Here’s an example of the usage:

static void Main(string[] args) {
  dynamic dict = new DynamicDictionary();

  dict.Foo = "Some Value";  // Compare to dict["Foo"] = "Some Value";
  dict.Bar = 123;           // Compare to dict["Bar"] = 123;

  Console.WriteLine("Foo: {0}, Bar: {1}", dict.Foo, dict.Bar);
  Console.ReadLine();
}

That’s kind of neat, and the code is very simple. To make a dynamic object, you have the choice of either implementing the IDynamicMetaObjectProvider interface or simply deriving from DynamicObject. I chose this second approach in this case because it was less work. Here’s the code.

public class DynamicDictionary : DynamicObject {
  Dictionary<string, object> 
    _dictionary = new Dictionary<string, object>();

  public override bool TrySetMember(SetMemberBinder binder, object value) {
    _dictionary[binder.Name] = value;
    return true;
  }

  public override bool TryGetMember(GetMemberBinder binder, 
      out object result) {
    return _dictionary.TryGetValue(binder.Name, out result);
  }
}

All I’m doing here is overriding the TrySetMember method which is invoked when attempting to set a field to a value on a dynamic object. I can grab the name of the field and use that as the key to my dictionary. I also override TryGetMember to grab values from the dictionary. It’s really simple.

One thing to note, in Ruby, there really aren’t properties and methods. Everything is a method, hence you only have to worry about method_missing. There’s no field_missing method, for example. With C# there is a difference, which is why there’s another method you can override, TryInvokeMember, to handle dynamic method calls.

What havoc can we wreack with MVC?

So I have this shiny new hammer in my hand, let’s go looking for some nails!

While I’m a fan of using strongly typed view data with ASP.NET MVC, I sometimes like to toss some ancillary data in the ViewDataDictionary. Of course, doing so adds to syntactic overhead that I’d love to reduce. Here’s what we have today.

// store in ViewData
ViewData["Message"] = "Hello World";

// pull out of view data
<%= Html.Encode(ViewData["Message"]) %>

Sounds like a job for dynamic dictionary!

Before I show you the code, let me show you the end result first. I created a new property for Controller and for ViewPage called Data instead of ViewData (just to keep it short and because I didn’t want to call it VD).

Here’s the controller code.

public ActionResult Index() {
    Data.Message = "<cool>Welcome to ASP.NET MVC!</cool> (encoded)";
    Data.Body = "<strong>This is not encoded</strong>.";

    return View();
}

Note that Message and Body are not actually properties of Data. They are keys to the dictionary via the power of the dynamic keyword. This is equivalent to setting ViewData["Message"] = "<cool>…</cool>".

In the view, I created my own convention where all access to the Data object will be html encoded unless you use an underscore.

<asp:Content ContentPlaceHolderID="MainContent" runat="server">
 <h2><%= Data.Message %></h2>
  <p>
    <%= Data._Body %>
  </p>
</asp:Content>

Keep in mind that Data.Message here is equivalent to ViewData["Message"].

Here’s a screenshot of the end result.

dynamic-mvc

Here’s how I did it. I started by writing a new DynamicViewDataclass.

public class DynamicViewData : DynamicObject {
  public DynamicViewData(ViewDataDictionary viewData) {
    _viewData = viewData;
  }
  private ViewDataDictionary _viewData;

  public override bool TrySetMember(SetMemberBinder binder, object value) {
    _viewData[binder.Name] = value;
      return true;
  }

  public override bool TryGetMember(GetMemberBinder binder,
      out object result) {
    string key = binder.Name;
    bool encoded = true;
    if (key.StartsWith("_")) {
      key = key.Substring(1);
      encoded = false;
    }
    result = _viewData.Eval(key);
     if (encoded) {
       result = System.Web.HttpUtility.HtmlEncode(result.ToString());
     }
     return true;
  }
}

If you look closely, you’ll notice I’m doing a bit of transformation within the body of TryGetMember. This is where I create my convention for not html encoding the content when the property name starts with underscore. I then strip off the underscore before trying to get the value from the database.

The next step was to create my own DynamicController

public class DynamicController : Controller {
  public dynamic Data {
    get {
      _viewData = _viewData ?? new DynamicViewData(ViewData);
      return _viewData;
    }
  }
  dynamic _viewData = null;
}

and DynamicViewPage, both of which makes use of this new type.

public class DynamicViewPage : ViewPage {
  public dynamic Data {
    get {
      _viewData = _viewData ?? new DynamicViewData(ViewData);
      return _viewData;
    }
  }
  dynamic _viewData = null;
}

In the Views directory, I updated the web.config file to make DynamicViewPage be the default base class for views instead of ViewPage. You can make this change by setting the pageBaseType attribute of the <pages> element (I talked about this a bit in my post on putting your views on a diet).

I hope you found this to be a fun romp through a new language feature of C#. I imagine many will find this to be an abuse of the language (language abuser!) while others might see other potential uses in this technique. Happy coding!

Tags: dynamic, aspnetmvc, C#, DLR

Comments