Framework Madness!

And other adventures C# and asp.net …

Asp.net MVC 2: Creating a SimpleValuesModelBinder

leave a comment »

I have been thinking about what it would take to make a custom model binder for the PayPal IPN service the past week or so, and tonight I finally had the chance to look into it. The goals is simple – I just want to pass the IPN form values to a model that has properties with different names. I don’t want to do any custom model binding, perhaps a small bit of string parsing but that’s about it.

I looked into model binding at the MSDN library, Steve Sanderson’s book, and the TekPub MVC series, they all were good, but what I wanted to do was simple. On thing I did get out of it was that I would need both a custom ModelBinder implementing IModelBinder and possibly a custom ValuesProvider as well.

Finally, I looked into the MVC source for the DefaultModelBinder to see how it worked and came up with pure gold. Here is the basic premise of the solution:

  • Create an abstract model binder class that uses the DefaultModelBinder class to do its work.
  • Leverage the NameValueCollectionValueProvider class to provide simple Key/Value pairs to the DefaultModelBinder .
  • Create a custom copy of the supplied ModelBindingContext that connects the binder and provider together.

Here’s the code with comments:

Code Snippet
//this allows for supplying values using a simple key/value pair collection
    ////all complicated binding set up is abstracted
    //DefaultModelBinder is used for ModelBinding
    //NameValueCollectionValueProvider is used as the ValueProvider
    public abstract class SimpleValuesModelBinder : IModelBinder
    {

        protected abstract void AppendValues(ControllerContext controllerContext, NameValueCollection collection);

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            //first we want to call AppendValues
            //the collection is supplied to the SimpleValuesProvider
            //we use the NameValueCollectionValueProvider because it uses key/value pairs
            var collection = new NameValueCollection();
            AppendValues(controllerContext, collection);

            //create a custom binding context
            //we pass thru properties except the value provider
            //we use the new TestValueProvider here
            var customContext = new ModelBindingContext()
            {
                ModelMetadata = bindingContext.ModelMetadata,
                ModelState = bindingContext.ModelState,
                PropertyFilter = bindingContext.PropertyFilter,
                ValueProvider = new NameValueCollectionValueProvider(collection, CultureInfo.CurrentCulture)
            };

            //pass our custom binding context the default binding model and bind
            return new DefaultModelBinder().BindModel(controllerContext, customContext);

        }
    }

 

Creating a custom model binder from this class is very simple. Here is a simple class:

Code Snippet
public class TestModel
    {
        public string Value { get; set; }

        public DateTime Current { get; set; }
    }

… and here is custom binder using the abstract class above:

Code Snippet
public class TestModelBinder : SimpleValuesModelBinder
    {

        protected override void AppendValues(ControllerContext controllerContext, NameValueCollection collection)
        {
            collection["Value"] = Guid.NewGuid().ToString();

            collection["Current"] = DateTime.Now.ToString();
        }
    }

The resulting custom model binder is very simple. All you have to do is override one method, AppendValues, and your done. In this case I am supplying arbitrary values as a test, but you can leverage any values you want from the ControllerContext parameter.

Of course set up is easy too, ASP.net MVC allows several ways to do this. For a test, I’ll just apply to a parameter in an action method.

Code Snippet
public ActionResult Index([ModelBinder(typeof(TestModelBinder))]TestModel Test)
        {
            return Content(string.Format("Value: {0}; Current: {1}",Test.Value,Test.Current));
        }

 

And the the less than glamorous result:

image

So, if your looking to do some simple value parsing this may help.

Advertisements

Written by Lynn Eriksen

August 22, 2010 at 4:16 am

Posted in Uncategorized

Tagged with

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: