JSON Model Binder doesn’t bind nullable properties in MVC 3

Model binding in the ASP.NET MVC framework is one of the features that make developers life easy. As you may know, a model binder in MVC provides a simple way to map posted form values to a .NET Framework type and pass the type to an action method as a parameter. In this post I’m not going to dig into the concepts of model binding as you can get a lot of information in many blogs.

One of the model binders in MVC 3 is JSON model binder. It is clear that it tries to bind json objects passed into the controller action via ajax call. To clarify the issue let’s start with a simple scenario.
Assume that we need to pass Student json object to a controller action. Here is the Student class:

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public long? Grade { get; set; }
}

The action is called Save and accepts a parameter of type Student:

[HttpPost]
public ActionResult Save(Student student)
{
   // save the student 
   return View();
}

We are using jquery for ajax call. Just put a button on the view and bind the click event using following code:



So, on button click event we create a student object and initialize its properties. Then post that object into the Save action of the Student controller. Now put a breakpoint in action code and run the project. If you look at the student parameter passed into the Save action you will find out that the model binder has created the Student object and assigned all the properties except Grade!



Is that a bug? Yes it is! If you just change the Grade property type from long? To long you will see that the issue solved! I don’t know why the model binder cannot bind the nullable properties and we have to wait to see if it’s solved in the next version. However we cannot stop our projects and have to find a solution.

AtionFilterAttribute is our hero here. As you may know, we can apply a filter before executing the Action in the request pipeline of MVC. Please look at the documentations in MSDN for further info.
ActionFilter gives us the ability to inspect the executing context of the action (ActionExecutingContext). In this context, we have access to HttpContext and the HttpRequest. Also, we have read and write access to the action parameters. What we can do is to bind the incoming JSON data to the action parameter. Here is the code for our custom action filter:

public class JsonModelBinder : ActionFilterAttribute
    {
        public JsonModelBinder()
        {
        }
        
        public Type ActionParameterType { get; set; }
        public string ActionParameterName { get; set; }
        
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            HttpRequestBase request = filterContext.HttpContext.Request;
            Stream stream = request.InputStream;
            stream.Position = 0;
            filterContext.ActionParameters[ActionParameterName] =
                        (new DataContractJsonSerializer(ActionParameterType)).ReadObject(stream);
        }
        
    }

As you see, I’ve used the DataContractJsonSerializer to deserialize the object. This type is defined in System.Runtime.Serialization.Json namespace and you have to add a reference to System.Runtime.Serialization to use it.
The important assumption here is that the InputStream contains a JSON data. ActionParameterName and ActionParameterType are two properties that we have to initialize them when decorating the action with this attribute:

[HttpPost]
[JsonModelBinder(ActionParameterName="student", ActionParameterType=typeof(Student)]
public ActionResult Save(Student student)
{
     // save the student 
     return View();
}

Now let’s run the project again and look at the action parameter:


Bingo!

No comments:

Post a Comment