Friday 9 October 2009

Pass data simply; learning from jQuery and ASP.NET MVC

One of the really nice things that is common to both jQuery and ASP.NET MVC is how it passed multiple values around. They both avoid the complexity of things like dictionaries (or alternatively forcing specific types) by letting the caller use ad-hoc objects; by which I mean either a jQuery “post” method like:

    $.post("test.php", { name: "John", time: "2pm" } );

Or an ASP.NET MVC ActionLink of the kind:

    <%= Html.ActionLink(category.CategoryName,
new { action="List", id = category.CategoryName})%>

In both cases, we are passing caller-specific data (not callee-specific data, so optional / named parameters wouldn’t help), but without the pain that we might have used in the past. Of course, we could also pass it one of our domain entities, and as long as the names all matched up, it wouldn’t care. Which is nice.

I’m going to concentrate on the second of these mainly – this is a hugely versatile tool; and while it shouldn’t be used for every job, it sure is handy. Here’s an example of using the same approach for a more readable Format syntax:

    string s = Format("You are {age} years old and your last name is {name}",
new {age = 18, name = "Foo"});

There are a myriad of opportunities for this type of use. My main aim here is just to show some of the possibilities – and let your collective imaginations run wild. For example, to bring the jQuery “post” approach into regular C#:

    using (WebClient client = new WebClient())
{
byte[] resp = client.Post(destUri, new {
id = 100021,
dob = DateTime.Today,
caption = "Hello world"
});
string html = client.Encoding.GetString(resp);
}

With supporting code:

    public static class WebClientExtensions
{
public static byte[] Post(this WebClient client,
string address, object values)
{
if (client == null)
throw new ArgumentNullException("client");
if (values == null)
throw new ArgumentNullException("values");

NameValueCollection valueLookup =
new NameValueCollection();
foreach (PropertyDescriptor prop in
TypeDescriptor.GetProperties(values))
{
object val = prop.GetValue(values);
string sVal = prop.Converter
.ConvertToInvariantString(val);
valueLookup.Add(prop.Name, sVal);
}
return client.UploadValues(address,
WebRequestMethods.Http.Post, valueLookup);
}
}

Sure, this is just a basic example and there are lots of things I could do to provide more flexibility, but it shows the key points.