Noah Blumenthal's Blog

November 7, 2008

Uri, ModelBinders, and UpdateModel()

Filed under: ASP.NET MVC — noahblu @ 12:25 am

So you’re developing an application using ASP.NET MVC and you got a Person class with properties:

string FirstName;
string LastName;
string Email;
Uri Webpage;

And of course you have a form that collects these 4 pieces of information.  And you build an Action in your PersonController called Create like so:

public ActionResult Create(FormCollection form){
Person p = new Person();
UpdateModel(p, form);
}

and you run it thinking you’re done and ready to go home and…. get an Exception on the line UpdateModel(p, form).  Why?

The answer has to do with ModelBinders and the IModelBinder interface and how UpdateModel() works.  That was something I couldn’t find too much information about so I thought I would write this post.

Basically, the MVC framework provides you with a cool method called UpdateModel that will take all the data from an IValueProvider (not gonna get into this right now) and update the model you send it with that data.  If the model is a complex type, it will iterate over all the properties and try to set them automagically.  But it isn’t actually magic.  The reason why your UpdateModel(p, form) line is crapping out is because the ModelBinder doesn’t know how to transform a string (e.g. “http://www.google.com”) into an object of type Uri.

The nice thing is that you can add your own custom binders by extending IModelBinder.  Here’s the code I used for UriModelBinder:

internal class UriModelBinder : IModelBinder
{
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != null && !string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
{
Uri url = new Uri(valueProviderResult.AttemptedValue);
return new ModelBinderResult(url);
}
return new ModelBinderResult(null);
}
}

* keep in mind that this will throw an error if the url is not formatted properly (because that’s what happens when you try instantiating a new Uri with an improperly formatted url) but will set the Uri to null if the string representing it is blank.  This is intentional, but not necessarily what YOU want.

Very simple here.  Just get the string value that we will use to set the property value and check if it’s null or empty.  If so, just return null.  If not, create a new Uri using the attempted value and return that.

Now to get this to work we just need one more thing — an entry in the Global.asax to register this Binder (otherwise it just sits there and nobody knows about it):

ModelBinders.Binders.Add(typeof(Uri), new UriModelBinder());

that means — “use this UriModelBinder to convert values to type Uri”.

Pretty cool.  You could also create a binder for the entire Person object using this same basic idea.  Google that and you’ll see numerous examples.

Here’s a free one — I also built a binder for DateTime? (that’s Nullable DateTime):

internal class NullableDateTimeModelBinder : IModelBinder
{
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != null && !string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
{
DateTime date = DateTime.Parse(valueProviderResult.AttemptedValue);
return new ModelBinderResult(date);
}
return new ModelBinderResult(null);
}
}

Pretty simple, huh?

1 Comment »

  1. […] I blogged a while ago about creating a UriModelBinder to bind a url string to a Uri object (see https://noahblu.wordpress.com/2008/11/07/uri-modelbinders-and-updatemodel/) but things have changed since then (I think that was RC1, but I don’t […]

    Pingback by UriModelBinder for ASP.NET MVC RTM « Noah’s Blog — March 23, 2009 @ 4:54 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.