Model Binding in MVC
Understanding Model binding in MVC is important for understanding important scenarios such as data validation.
While working in a MVC application we come across the different components such as the controller,action methods, application routes and the model.These all are no doubt important components in any MVC application ,but there is one other component which is as much important and works behind the scenes ,the Model Binder.
We define the action methods in a controller as :-
public ActionResult EmployeeDetails(int empId) { //method body return View(); }
In the above method the parameter empId ,of type integer, is populated with the id value from the request.We may have provided the empId value in querystring or as a part of form field but what matters is that we have not mapped the value to the action method parameter or provided any instructions to map a particular form field or querystring value to our action method parameter.This mapping is done automatically by the default Model binder.
We can demonstrate the model binding process with the below diagram
How data is mapped in Model Binding
When we define action method parameters ,the values are auto populated in those parameters by the default model binder.There are two main points to consider in the Model Binding process
- The name of the field defined in the request should be the same as the name of the action method parameter
- The request data should be convertible to the action method parameter
So if we have defined an action method parameter with the name empId then that field should be present in any of the different request data locations.And if we have defined empId as integer parameter then the supplied value should be convertible to an integer.We can not for instance supply a string value for empId otherwise a runtime exception will be thrown.
Different Request Data Sources used in Model Binding
The Model binder relies on another component to provide it the request values,the Value provider.The value providers searches the different request sources for the data and then provides the data to the Model Binder which then binds the data to the action method parameter . The different locations which the vale providers search for the data are.
- Form
- Route
- Query String
- Files
The above request data sources are searched in order.So lets say if a value is found in the Form then other locations are not searched for the value.Model binder searches the above locations by the parameter name.
So the different request locations are searched by the Value providers and the values are provided to the Model Binder.
One important thing to consider about Model Binding in MVC is that we can pass any number of parameters to the action method .If the names of the parameters matches with the request data then the model binder will populate all the parameters with the information passed in the request ,but sometimes it can be difficult to manage.So we can define a method as:-
public ActionResult Details(int empId,string name,string department,string address,DateTime dateOfJoining,string status) { return View(); }
The above method definition is fine but is not easy to manage. Tomorrow if a new input field is added in a form then we need to add the parameter in the method.Also from the method signature we are not getting the clear idea about the entity being passed to the method.
Another approach and which is a better way to pass the data to the action method is moving all the action method parameters in an entity (if such entity does not exists already) and pass the entity as a parameter.The entity acts as a Data Transfer object here.We can encapsulate all the above method parameters in a Employee class as:-
public class Employee { int empId{get;set;} string name{get;set;} string department{get;set;} string address { get; set; } DateTime dateOfJoining { get; set; } string status { get; set; } }
public ActionResult Details(Employee emp) { return View(); }
Passing Collection to the Action method
One useful scenario of model binding in mvc is when we need to pass a collection to our method.Suppose we are using the employee class which is defined as
public class Employee { public int empId { get; set; } public string name { get; set; } public string department { get; set; } public string address { get; set; } public DateTime dateOfJoining { get; set; } public string status { get; set; } public List<string> reportees { get; set; } }
Then to allow the user to provide the information for the Employee class ,which includes the information for the reportees collection as well,we can define the input fields in our view as:-
@using (Html.BeginForm("Details", "Home")) { <table > <tr> <td>@Html.LabelFor(x=>x.name)</td> <td>@Html.TextBoxFor(x=>x.name) </td> </tr> <tr> <td>@Html.LabelFor(x=>x.department)</td> <td> @Html.TextBoxFor(x=>x.department)</td> </tr> <tr> <td>@Html.LabelFor(x=>x.address)</td> <td>@Html.TextBoxFor(x=>x.address)</td> </tr> <tr> <td>@Html.LabelFor(x=>x.dateOfJoining)</td> <td>@Html.TextBoxFor(x=>x.dateOfJoining)</td> </tr> <tr> <td>@Html.LabelFor(x=>x.status)</td> <td>@Html.TextBoxFor(x=>x.status)</td> </tr> <tr> <td>@Html.LabelFor(x=>x.reportees)</td> <td><input type="text" name="reportees" /> <input type="text" name="reportees" /> <input type="text" name="reportees" /></td> </tr> <tr> <td><input type="submit" name="submit" /></td> </tr> </table> }
The above markup renders the following page
After submitting the above form data if we observe the Employee parameter which we are passing to the action method we can see that the reportees list is automatically populated.Above we have just given the same name to the reportees input fields and the collection gets auto populated with these input fields.
If we had to populate the collection ourselves then not only it is tedious process but also error prone.
Model Binding Properties
To customize the default model binding we can use the Bind attribute which has three useful properties
- Include Specifies comma separated list of properties to bind to the parameter object
- Exclude Specifies comma separated list of properties which we don’t want to bind to the parameter object
- Prefix Allows the action method parameter name to be different from the request field.
We will take our previous example and fill the different fields for the employee model object and submit the form and see the effect of apply the above attributes to the action method parameter.
We fill the form with the values and submit
All the employee parameter properties are populated by default
Include If we set the Include property of the Bind attribute to Name then rest of the request values are ignored and only name employee property is populated by the default model binder
Exclude If we set the exclude property of the Bind attribute to Name then except the name property rest of the properties are populated by the default model binder
Prefix We can use the Prefix property to match the request field to the Prefix property value instead to the method parameter.We can define an input field with the name firstName and our parameter will still be populated by the firstName field,even though our method parameter is called name,by using the Prefix property of Bind attribute.
@Html.LabelFor(x=>x.name)
public ActionResult Details([Bind(Prefix="firstName")]string name) { return View(); }
So we can use the default model binder to bind the different parameter types as well as the parameter collections.If we need to define our own custom logic then we can create a custom Model Binder.
The main advantages of Model Binding in MVC are
- It simplifies accessing the request information in the action method.
- It makes the action methods easier to unit test as the action method is not relying on any framework component such as Request or Response.