Extending ASP.NET MVC Model Binding Mechanism

Hey guys,

In the last posts I’ve talked about the use of Moq framework when writing ASP.NET MVC unit tests. Today, I will be talking about ASP.NET MVC Model Binding mechanism and how to extend it to make your life easier when writing testable web applications. [I have written a similar post on SharpShooters blog, but it is in portuguese]

Basically, Model Binding is the mechanism that ASP.NET MVC framework uses for mapping HTTP Request variables into Actions’ parameters. In other words, every time you submit a form to a Controller, the Model Binding mechanism passes through all variables submitted trying to match them with Actions’ parameters using a very specific standard. The standard is “parameterName.PropertyName”. To illustrate how things happen, let’s use a small example. Imagine that we want to implement a blog platform which allows the creation of posts with title and content. Model, View and Controller for creating it are illustrated below:

Post.cs

PostWithoutUser

Create.aspx

CreateView

PostController.cs

CreateControllerWithoutUser

As you already know, the Create Action receives newPost parameter with all the information submitted in the form. However, if the ASP.NET MVC Team hadn’t implemented Model Binding, we would need to type something like this:

Create.aspx

RequestCreateView

PostController.cs

RequestCreateController

In short, Model Binding is responsible for binding values passed in HTTP Requests to Actions’ parameters.

Now that we already know what Model Binding is about, we can extend it to bind any parameter we want. Imagine that we want to add a User to a Post. Class and Controller would have to change to something similar to this:

Post.cs

PostWithUser

Controller.cs

PostControllerWithUser

Although this code works, it is very difficult to test since I need to mock User.Identity.Name. I can mock it using a technique presented by Michael Feathers in his excellent book Working Effectively With Legacy Code which basically involves two steps:

1. Extract the piece of code in a protected virtual function;

2. Create a mock which inherit from PostController and overrides the protected function by returning a specific username;

PostController class would result in the code below.

PostController.cs

PostControllerProtected

And here is the test for the controller class.

MockPostController.cs

MockPostController

MockControllerTest.cs

PostControllerTestProtected

Now, the test pass 🙂

However, every time that a User is referenced by other class, this approach is needed. Imagine we have another entity, for example Comments, which has a reference to User as well. The same solution would be used to test CommentsController class. It would be great if we could pass the User as a parameter in PostController.Create action, wouldn’t be? Something like this:

PostController.cs

PostControllerWithTwoParameters

To do that, we need to extend ModelBinding mechanism so it can bind the value of a User type. This is really simple. All we have to do is implement IModelBinder interface and register it in the Global.asax. The code below shows the extension.

UserModelBinder.cs

UserModelBinder

Global.asax

Global

Everything is now configured and we don’t need to worry about retrieving the logged user anymore.

In adittion to that, it’s much more easy to test PostController class. We can create a new user and pass it as parameter, as shown below.

PostControllerTest.cs

PostControllerTestWithTwoParameters

To conclude, Model Binding is a very importante feature of ASP.NET MVC framework, saving us a lot of work. It is also extensible and allows us to create our own bindings and testable applications.

Hope it’s usefull for you someday 🙂

See you,
Fernando

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

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

Google+ photo

You are commenting using your Google+ 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 )

w

Connecting to %s