MVC Model Binding
Details and extensibility of the ASP.NET MVC model to bind posted data
August 25, 2010
In too many ASP.NET MVC application demos, the presenter begins by creating a LINQ-to-SQL or an Entity Framework model. Frequently, the presenter then refers to these models as the “model” of the application. Some presenters copy the DBML or EDMX files into the Models folder that Visual Studio creates for us automatically. It seems so easy and natural—that’s the model of our ASP.NET MVC application. Well, not exactly.
It's essential that you—as an architect or developer—understand that, in general, an ASP.NET MVC application is made of three distinct models, each playing a specific role. Using three distinct sets of classes is the only proven way to deal with particularly complex applications where the needs of the user interface don’t match nicely with the representation of the data you have in the middle tier. Note that this mismatch is not necessarily a bad thing that results from mistakes you made in design. It could simply be due to the need for rendering a user-friendly interface while keeping data organized in a DBA-friendly way on the databases. Most of the time, you just need to pass the user interface aggregates of entities that don’t exist on the server. It all depends on the use-cases, and data transfer objects exist just to serve this scenario.
This article, however, is not about design patterns for modeling the business layers of an application. I focus, instead, on ASP.NET MVC model binding. Model binding is a built-in feature of ASP.NET MVC that helps you map posted data to handy classes for easier processing. When it comes to defining the set of classes to use in model binding, though, you might be tempted to use a single collection of classes—those you use in the back end for actual persistence. This may or may not be a good choice. However, understanding the types of models you can recognize in an ASP.NET MVC application is key.
Flavors of Models in ASP.NET MVC
In ASP.NET MVC, the term “model” is used to mean three distinct things: the representation of the data being posted to the controller, the representation of the data being worked on in the view, and the representation of the domain-specific entities operating in the business tier.
In relatively simple scenarios (like many of the Web applications you're commonly asked to write), it might be acceptable to employ a single set of classes—that is, a unique model. However, choosing a simplified design is safe only when it doesn’t produce any loss of generality. Figure 1 shows graphically where each model logically belongs.
Of the three models, the least understood is the Posted Data Model (PDM). In ASP.NET MVC, a request is first received by a controller method. The request is then passed to some processor (ideally, an application service); then it's converted into a response for the browser to receive. Each step needs its own specific representation of data; hence the three models shown in Figure 1. You can certainly simplify things and reuse models (or portions thereof) when acceptable. However, from a design perspective you can’t ignore the three models. The PDM is the set of classes through which controller methods receive their input data from HTML forms.
Data for an Action Method
The basic question to answer: How would I like to pass any input data to my controllers? You can choose to pass no explicit parameters and let the method find any data of interest by performing a direct access to the Request collections—Form, QueryString, Cookies, and the like. An alternate approach entails using the default model binder class of ASP.NET MVC and having it bind posted data to explicit parameters in the signature of action methods. The binding algorithm is based on a logic that matches parameter names with entries in the dictionary of posted data. You would have an action method like that shown below:
public ActionResult Update(Int32 id, Int32 age, String name, String email)
\{
:
\}
The call to the Update method is prepared by the ASP.NET MVC HTTP handler in charge of any requests for the application. The ASP.NET MVC infrastructure will look into the method signature and try to match any of the listed formal arguments to any of the input parameters associated with the ongoing request. The match is case-insensitive. An HTTP request for an ASP.NET MVC application carries a variety of data from a number of value providers—form data, query string parameters, route values, values from posted files. The framework includes a class named ValueProviderFactories that holds four default value providers, as listed in Figure 2.
A value provider is a class that implements the IValueProvider interface and makes itself responsible for providing request-specific values for a given key:
public interface IValueProvider
\{
bool ContainsPrefix(string prefix);
ValueProviderResult GetValue(string key);
\}
Each parameter in the signature of an action method will be resolved by contacting the providers in Figure 2 in the order in which they are listed. Search stops when the first provider claims it can resolve the parameter. You can create custom value providers as well. The canonical example that you can find discussed in several blog posts is a cookie value provider that attempts a match between cookie strings and parameters. You register a custom value provider in global.asax, as shown below:
protected void Application_Start()
\{
:
RegisterRoutes(RouteTable.Routes);
ValueProviderFactories.Factories.Add(new YourCookieValueProviderFactory());
\}
Actually, you need to create a factory class and add that to the list of value provider factories. You can find a good example at www.dotnetguy.co.uk/post/2010/03/06/ASPNET-MVC-working-smart-with-cookies-e28093-Custom-ValueProvider.aspx.
Resolving Parameters
The nice thing about model binding is that you can map any posted data to an individual parameter (of a scalar type) in the action method signature, as well as to a member of a complex type. Let’s consider the following action method:
public ActionResult Update(Contact contact)
\{
:
\}
The Contact type is defined as:
public class Contact
\{
public Int32 Id \{ get; set; \}
public Int32 Age \{ get; set; \}
public String Name \{ get; set; \}
public String Email \{ get; set; \}
\}
As long as a match is found between the name of a posted piece of data (recognized through any of the registered value providers) and a public member of Contact class, the binding happens. Figure 3 shows what you see during a debugging session.
The members of the Contact class receive the value stored in any posted data with a matching name. For example, if the HTTP request originates from a form like that shown below, all members will be set correctly as shown in Figure 3.
<% Html.BeginForm("Edit", "Contact"); %>
:
<% Html.EndForm(); %>
Note that member coverage doesn’t have to be complete. In other words, you can have a form that contains matching elements for a few of the type members. Remaining members can be resolved by using other value providers (i.e., query string) or by leaving them null—in which case, you must change the type to Nullable; otherwise, an exception will be thrown. Null is not a valid value unless Nullable is used.
Customizing the Binding Process
As mentioned, DefaultModelBinder is the class doing the magic of model binding. It's all automatic; all you need to do is define the signature of action methods and adjust the view, URL, and query string to carry any desired piece of data.
Automatic model binding stems from a convention-over-configuration. Conventions, though, may sometimes reserve bad surprises. If, for some reason, you lose control over the posted data (i.e., the data has been tampered with), it can result in undesired binding—any posted key/value pair will, in fact, be bound as long as a match is found. You can use some attributes to mitigate the automation of DefaultModelBinder and gain yourself more control over the process. In particular, you should look at the Bind attribute and use it with the action method signature. Needless to say, you want to use Bind on complex types. If you don’t want to bind a scalar parameter, don’t add it to the signature. Here’s an example:
public ActionResult Update(\[Bind(Exclude="Id")\] Contact contact)
\{ ... \}
The Bind attribute supports three properties (shown in Figure 4).
The use of Exclude and Include properties are self-explanatory; the Prefix property deserves some discussion. When posted data is mapped to properties of a class, the match can be found not only on the name, but also on the name with some prefix. Let’s consider the following markup:
<% Html.BeginForm("Edit", "Contact"); %>
:
<% Html.EndForm(); %>
The value of the input field will be bound to the first signature shown below, but not the second:
public ActionResult Edit(Contact contact)
public ActionResult Edit(Contact theContact)
Both signatures will work if you use an ID without prefix:
<% Html.BeginForm("Edit", "Contact"); %>
:
<% Html.EndForm(); %>
By default, the binder resolves bindings when no prefix is used or when the prefix matches the name of the formal parameter. The point is that sometimes you need to disambiguate the ID of the element and some sort of prefix becomes necessary. The Prefix property of the Bind attribute allows you to do just that and instruct the binder to expect a particular prefix instead of the default one. Note that the use of the prefix is either total (applies to all form fields) or nothing (no form field has a prefixed name).
Finally, note that while the Bind attribute is often applied to individual parameters on a controller method, you can even define it on any class being used in the signature.
Binding to Collection Types
What if the argument that a controller method expects is a collection? For example, can you bind the content of a posted form to a parameter of type IList? The DefaultModelBinder class supports this scenario as well, but requires some attention to the definition of IDs in the markup.
To ensure that a collection of values is passed to a controller method, you need to ensure that elements with the same ID are emitted to the response stream. The ID, then, must match the controller method’s signature according to the normal rules of the binder. The markup shown in Figure 5 is acceptable.
Once posted to the server, the content of the input fields in the listing is bound successfully to the following signature:
public virtual ActionResult Update(IList customerInfo)
\{ ... \}
Note that indexes you use in the markup are to be consecutive. If a missing index is found, the binder stops working and no successive element is added to the bound collection. Prefixes are supported as well, with the same rules discussed earlier for individual complex types.
In ASP.NET MVC and ASP.NET Web Forms, posted data arrives within an HTTP packet and is mapped to a collection on the Request object. To offer a nice service to developers, ASP.NET then attempts to expose that content in a more usable way. In ASP.NET Web Forms, the content is parsed and passed on to server controls; in ASP.NET MVC, the content is bound to parameters of the selected controller’s method. The process of binding posted values to parameters is known as model binding and occurs through a registered model binder class.
The model binder class offers a number of powerful services and is configurable to a good extent. Rest assured that if you’re having trouble mapping posted values to your expected hierarchy of types, considering a custom model binder might be wise. Custom model binders will be the topic of a future article.
Read more about:
MicrosoftAbout the Author
You May Also Like