Friday, October 14, 2011

ASP.NET MVC Model Binding - Part1

Introduction

ASP.NET MVC has many great features. I have been playing around with ASP.NET MVC, and gone through couple of interesting things as part of Model Binding. Here I am putting together all my findings, and explain with a simple example. I am sure this will save a lot of time for people who are out there, looking to understand, how exactly ASP.NET MVC Model binding works.
In ASP.NET MVC, Model Binding is, mapping the HTTP request data directly to Action method parameters and .NET objects (a Model).

Using the Code 

As this is going big, I am splitting this into parts. In this part, I am covering ASP.NET MVC object model binding, Parameter binding using Query String parameters, and controlling Model binding using Bind Attribute.
Below is the code structure:
CodeStructure.JPG
To make it simple, and to concentrate more on Model binding, I am taking a simple example,  which is having Employee Model, and EmployeeController Control, and two Views EmployeeView1, EmployeeView2.
When we run the above code, it displays Employee Add/Edit page (Adding/Editing to database is out of scope for this article), EmployeeView1 view, like below:
EmployeeView1.JPG
'Using Query Strings' link is to demonstrate model binding using Query String parameters, 'Control Model Binding' link is to demonstrate how we can control model binding. Controlling Model Binding gives control on what Model properties can bind with incoming HTTP request.
I have used VS 2008 SP1, ASP.NET MVC 2 for this sample, recommended to use the same version.
Here is how model binding works, for e.g., consider the below request:
 POST: Route Values, Query String Parameters
Route Values decide the controller and the Action method is responsible for handling the request and the Query String Parameters basically map to the Action method parameters, this is explained in detail below. Here is the Employee Model:
public class Employee
{
    [DisplayName("Employee ID")]
    [Required(ErrorMessage = "ID Required")]
    public int EmpId { get; set; }
    [DisplayName("Employee Name")]
    [Required]
    public string EmpName { get; set; }
    [DisplayName("Designation")]
    [Required]
    public string EmpDesignation { get; set; }
    [DisplayName("Department")]
    [Required]
    public string EmpDepartment { get; set; }
    [DisplayName("Salary")]
    [Required]
    public float EmpSalary { get; set; }
}
The above model is decorated with Data Annotations to take care of simple validations. As part of this writing, we will not discuss much on validations.

Binding Style 1: Query String Parameters

Query String Parameters can directly map to the Action method parameters. Consider the below request which corresponds to the action method in the sample.
EmployeeController.cs
-------------------------

public ActionResult UpdateQueryString(int EmpId, string EmpName, 
   string EmpDepartment, string EmpDesignation)
{
    return View("EmployeeView1");
}
The above request directly maps each HTTP query string parameters EmpId, EmpName, EmpDepartment, EmpDesignation to Action method parameters respectively and updates the above EmployeeModel to contain these values. To observe this behavior, the attached sample renders the binded values in the EmployeeView1 view page, once you set the source code, run the application from Visual Studio, and click on the link Using Query Strings ; link. We will observe the updated fields on the view page. Have a look at the below snapshot.
EmployeeView2.JPG
We can generate the above HTTP request by using the HtmlHelper method like below. Observe the provided handy sample for more information.
<%= Html.ActionLink("Using Query Strings", "UpdateQueryString", 
new { EmpId = 1, EmpName = "Nick", EmpDepartment = "IT",
 EmpDesignation = "Director" })%>

Binding Style 2: Object Binding

We see this type of binding in Action methods most of the times. From HTTP request values will be mapped to Model attributes behind the scenes, to make it work we need to follow certain thumb rules. For e.g., consider the below code:
[HttpPost]
public ActionResult Update(Employee employee )
{
    if (!this.ModelState.IsValid)

    ModelState.AddModelError("Employee", "Model is Not Valid");
        return View("EmployeeView1");
}
and the View page contains the following strongly typed way of rendering the fields. For e.g.:
 <%= Html.TextBoxFor(model => model.EmpId) %>
 
The above code automatically binds the strongly typed 'EmpId' entered in the text box to the EmpId of the model (employee) and sends over this to Action method when we submit the form. Consider the below line, instead of strongly typed method like above, we can consider using the below method:
  <%= Html.TextBox("employee.EmpId")%>
Parameter name and property name combination pulls the values from HTTP input. The above method follows the prefix.PropertyName standard. prefix generally if we don't specify, it will be Action parameter name. In the above example, employee is the parameter for action method. Values entered in the EmpId text box will be mapped to EmpId property of the Model.
If we specify the prefix using Bind attribute for the action method like below:
[HttpPost]
public ActionResult UsingPrefix([Bind(Prefix = "emp")]Employee employee)
{
    if (!this.ModelState.IsValid)

    ModelState.AddModelError("Employee", "Employee Not Valid");
        return View("EmployeeView2");
}
then Helper method in the view page should look like below:
<%= Html.TextBox("emp.EmpId")%>
Click the submit button to observe the Object Model binding.

Controlling Model Binding

Sometimes, it may not be required to show all the fields in the View, but Model contains more properties than what a View shows. It is a potential candidate for injection attacks. To avoid this, we can control the Model binding from View to Model using the Bind attribute.
public ActionResult BindInclude
    ([Bind(Include = "EmpId,EmpName", Exclude = "EmpSalary")] Employee employee)
        {
            return View("EmployeeView1");
        }
Using the Bind attribute, we can white list Model Properties using Include param name, black list Model Properties using Exclude param name. In the above code, only EmpID, EmpName inputs from HTTP request will be mapped to employee model, if this results in invalid model state because of the validations set, then corresponding validations will fire automatically. I will write in the next part, about Collection binding, Collection binding objects, etc., how to do manual Model binding. I am sure this reading will save a lot of time for readers who are hunting for Model binding.

Disclaimer

This article uses a sample, which is not for direct production deployment. This is to give the reader an idea on how model binding works, it is individual responsibility to follow the necessary best practices while implementing the Model Binding.

No comments:

Twitter Bird Gadget