Model Binding in ASP.NET Core

Model binders work under the covers in ASP.Net MVC to provide the data for the action methods on a controller class. This data is held by the incoming HTTP request embedded into POST-ed form values, and maybe even the URL itself. Much magic is performed to get this right and, with some care, you can ensure that you can bind to object data, arrays and collections, as well as providing default values.

The term ‘Model binding’ refers to the unusual feature of ASP.NET MVC that allows you to map request information to named parameters. Every single request that hits the ASP.NET MVC application is served by executing an action, namely a public method on a controller class. At least in classic ASP.NET MVC, any controller class in a user’s application must inherit from a system-provided base class. This is different in ASP.NET Core where you can also have a plain old C# class as a controller. This feature is, in ASP.NET Core, referred to as POCO controllers. The only real difference between a POCO and non-POCO controller is in the way that HTTP-related data is passed in. In addition, you are in total control of the information that you get in a POCO controller, and so can avoid being forced to react to all that the framework might be handing you.

Model binding is an invisible layer of code available to both POCO and non-POCO controllers that maps HTTP-related information to public properties of the controller class. Mapping takes place on matching names. If the querystring contains a parameter named, say, CustomerId then a parameter declared on the action method with the same name (case doesn’t matter) will be automatically populated with the posted value. Model binding looks like magic but it results from some hard reflection work and conventions. The major benefit of model binding is that it makes your controller extremely clean; especially when the posted data is not a plain list of primitive values.

In this article, I’ll first review the core mechanics of model binding and then move on to consider how powerful it can be when you have lists of aggregated data to post from a form.

Basics of Model Binding

Model binding “binds” posted data to the data model exposed by the controller’s method signature. By default, the list of bindable values is not exhaustive but includes querystring values, form data and route values. Cookies and HTTP headers are not automatically bound but some room exists for customization. In general, any input parameters that comes to the server over a HTTP request can be processed by the model binding layer and passed as an argument to the controller. Here’s a quick example.

As you can see, it makes a whole world of difference to retrieve the ID of the customer to edit. With model binding you are served the ID as an integer right in the method. Otherwise, you have to use magic strings through the Request collection and do any necessary casting and conversion yourself.

Type matching is very important when it comes to model binding. If the controller maps an incoming parameter as an integer, and the passed value can’t be converted to an integer, then the application throws an exception. The hardest part is that exception like this is not easy to trap. Even if you use a try/catch block to wrap the entire body of the method, it won’t work. The problem is that model binding occurs before the controller method is invoked; so any exceptions that might occur are out of your control. The only workaround to avoid a nasty system error page is by using a global error handler, such as having some custom code in Application_Error.

Any data that travels over HTTP comes to you as a string and must be converted appropriately to become usable. Model binding offers to do that for you but takes away from you the control over possible null or conversion exceptions. There are some precautions you can take, however. If you’re expecting an integer value and foresee that it might be missing in some requests, then you’d do better by defining the receiving parameter to be nullable or provide a default value. Here’s an example.

Unlike a string, an integer is not a nullable type: Therefore, no exceptions will be raised for missing string parameters.

Using Complex Data Types in an Action Method

Automatic parameter resolution is not limited to primitive data types such as numbers and strings. It also works in the same way if you use a complex data type in the action method signature, as in the example below.

Model binding can bind posted values to public properties of the complex type used in the signature. An instance of the Customer class is built on the server in the early stages of request processing and then passed on to the action method. The same rules discussed for primitive types apply here but some extra rules. It is required, for example, that the bound complex type features a parameter-less constructor. The reason is that only in this way it is possible for the model binding layer to instantiate the class indirectly. In addition, all bindable members of the bound data type must have public get/set accessors. This is required in order to ensure that values can be copied appropriately from the HTTP context to the newly-created instance. Note that the base controller class provides a method called UpdateModel. This method can be used to perform model binding programmatically. The major benefit of this contrivance is that you can control possible exceptions.

The overall expressivity of the code is diminished because of the lack of explicit parameters on the signature. However, the programmer’s control over the entire process is greatly improved. Which types should you use to bind and receive request data? The easiest approach suggests you use one of the model classes. The problem is, which model? The acronym MVC stands for Model-View-Controller and the M refers to the collection of classes that form the application data model. The concept of the “model” needs more context. The data model is the model you use to persist data and read it back from storage. Optionally, the domain model is the model you use to express the behavior that is expected from classes. The domain model is distinct from the task of data persistence, and represents, instead, a domain-friendly way to express business logic. The view model is the model you use to display data to users. Typically, the view model has some intersection with the data model. Most likely, a view model class is a class with some properties being instances of data model classes. Finally, the input model is the model you use to receive data from requests. Breaking up the concept of a “model” into four distinct models is probably too granular for the real world: Aalthough conceptually correct, it might not be necessary in every application.

Rather than spoiling the data model classes to better capture incoming data it makes more sense to use ad hoc classes that reflect the structure of incoming data. Having said that, I must add that the model binding layer provides a couple of other powerful features that may reduce even further the need to have custom classes to map incoming data. ASP.NET MVC model binding supports collections of both primitives and complex types. Let’s find out more.

Binding to Arrays of Primitive Data

Sometimes you have HTML forms that collect multiple instances of the same data entity. A common example of this is an HTML form with input fields for several email addresses. Each email address is a plain string. On the controller’s side, you would ideally expect to receive an array of strings and model binding allows us to get just that at the cost of applying some HTML conventions.

As you can see, the signature of a controller method can host any combination of binding patterns: complex types, sparse primitive types and collections. For the model binding layer to group together in an enumerable object some related parameters it is required that the HTML input fields follow certain conventions. Here’s an example:

If multiple HTML input fields share the same name attribute then the model binding layer ends up processing the following data:

The model binding layer turns that string into an array of strings and binds that to any declared parameter with a matching name. An interesting point to consider is just the name of the input field you would use. In the code snippet above, I named all input fields “emails”. A plural name like that works beautifully on the controller’s side where you would expect to receive an array of strings. It doesn’t work that much on the HTML side where each input field treats a single email. There’s just one way to solve the issue and name variables appropriately on both sides. The idea is decoupling the name of the posted variable from the name of the mapped parameter. The Bind attribute just allows to do that.

You call the input field with the singular name of “email” and bind the singular posted name to a parameter with a pluralized name. As a margin note, consider that HTML allows you to use different IDs for the name and the ID attribute. Also, HTML sets requirements on characters allowed in an ID name (e.g., you can’t use square brackets) but it releases them for the name attribute. Just this characteristic allows to take model binding one step further: binding collections of custom complex types.

Binding to Collections

Suppose your HTML form collects multiple aggregates of information such as addresses. Realistically, you might render an address as below:

Moreover, an address might be part of a larger data structure such as a Customer:

Editing or creating a customer’s record posts to the server data that has to fit nicely into the above structure. The model you use for binding should accommodate the structure of the input form and the structure of the data model. Let’s assume, and it’s realistic to do so, that the input form matches the structure of the Customer class. When the form is posted, the server receives a collection of addresses. How does it work with model binding?

The above HTML structure will be matched nicely by the following controller method signature:

However, if you’re only interested in addresses, then you can go with the following as well:

In this case, however, the name attribute of the HTML input must be set to “addresses”. This approach is quite elegant and functional, but not perfect. In particular, it works nicely if you know exactly how many items populate the collection but it might fail otherwise. For example, if the sequence of indexes in the posted values has holes then binding fails. Indexes usually start from 0 but regardless of the starting index the bound collection is truncated at the first missing index. If you have addresses[0] and then addresses[2] and addresses[3] then only the first one will be automatically passed to the controller method. Enumeration, in fact, stops at first missing index.

Note that all data stored in the form is actually passed to the server in the context of the HTTP request. This is to say that on the server you still have all the data you need. Missing information refers exclusively to the information bound to the signature of the action method.

Sometimes, you find it easier to refer the posted data object by ID rather than index. This scenario can happen when the user interface allows you to select random records from a list to edit. Here’s an example:

Once posted, this collection of customers clearly results in a discrete sequence of indexes. However, a little known trick allows you to support also this scenario. You must add an extra piece of markup like below:

Using the pseudo property Index informs the model binding layer of the next chunk of possibly consecutive indexes to add to the resulting collection. The hidden field must be added before each array of input fields where indexing would produce a hole. The net effect of such a code is that you can now bind to the following:

The customers variable now will be a list of any customer that users chose to edit on the client.

Model Binding in ASP.NET Core

ASP.NET model binding is an important feature of ASP.NET MVC that is fully available also in ASP.NET Core. In the latest version of ASP.NET, though, some small changes not only make it even easier to use but also irons out some of the wrinkles discussed here. In particular, when you bind a value that is supposed to be an integer, it now uses the default value of the type (0 for integers) if no value is posted. This saves you from null exceptions as well as the need to assign a default value yourself. In addition, in ASP.NET Core the order of dictionaries the system looks for matching values is different. The system now first looks in form data, then in route values and finally in querystrings. Route values are, instead, the primary source in classic ASP.NET MVC.

ASP.NET Core also introduces many more attributes to control binding on controller parameters, but that just makes good fodder for another article!