28. Using Model Binding Flashcards
What is Model Binding?
Model binding is the process of creating the objects that action methods and page handlers require using data values obtained from the HTTP request.
Model binding is the process of creating .NET objects using the values from the HTTP request to provide easy access to the data required by action methods and Razor Pages.
Why isModel Binding useful?
Model binding lets controllers or page handlers declare method parameters or properties using C# types and automatically receive data from the request without having to inspect, parse, and process the data directly.
How is Model Binding used?
In its simplest form, methods declare parameters or classes define properties whose names are used to retrieve data values from the HTTP request. The part of the request used to obtain the data can be configured by applying attributes to the method parameters or properties.
Are there any pitfalls or limitations to Model Binding?
The main pitfall is getting data from the wrong part of the request. I explain the way that requests are searched for data in the “Understanding Model Binding” section, and the search locations can be specified explicitly using the attributes that I describe in the “Specifying a Model Binding Source” section.
Are there any alternatives to Model Binding?
Data can be obtained without model binding using context objects. However, the result is more complicated code that is hard to read and maintain.
Model binding example?
You can see model binding at work by using the browser to request
http://localhost:5000/controllers/form/index/5.
This URL contains the value of the ProductId property of the Product object that I want to view, like this:
The number 5 in the URL corresponds to the id segment variable defined by the controller routing pattern and matches the name of the parameter defined by the Form controller’s Index action:
…
public async Task Index(long id = 1) {
…
A value for the id parameter is required before the MVC Framework can invoke the action method, and finding a suitable value is the responsibility of the model binding system.
What do model binding systems rely on?
The model binding system relies on model binders, which are components responsible for providing data values from one part of the request or application. The default model binders look for data values in these four places:
•Form data (Data from a Form that the user has filled out)
•The request body (only for controllers decorated with ApiController)
•Routing segment variables
http://localhost:5000/controllers/Form/Index/5
•Query
http://localhost:5000/controllers/Form/Index?id=4
Each source of data is inspected in order until a value for the argument is found. The search stops after a suitable data value has been found.
Can ypu specify the source of model binding data, and if so how?
TipIn the “Specifying a Model Binding Source” section, I explain how you can specify the source of model binding data using attributes. This allows you to specify that a data value is obtained from, for example, the query string, even if there is also suitable data in the routing data.
Explain the binding of simple data types.
Listing below adds parameters to the SubmitForm action method defined by the Form controller method so that the model binder will be used to provide name and price values.
[HttpPost] public IActionResult SubmitForm(string name, decimal price) { TempData["name param"] = name; TempData["price param"] = price.ToString(); return RedirectToAction(nameof(Results)); }
The model binding system will be used to obtain name and price values when ASP.NET Core receives a request that will be processed by the SubmitForm action method. The use of parameters simplifies the action method and takes care of converting the request data into C# data types so that the price value will be converted to the C# decimal type before the action method is invoked. (I had to convert the decimal back to a string to store it as temp data in this example. I demonstrate more useful ways of dealing with form data in Chapter 31.)
Why does Data binding for simple types make it easy to extract single data items from the request?
Data binding for simple types makes it easy to extract single data items from the request without having to work through the context data to find out where it is defined.
What is the problem when Binding Simple Data Types in Razor Pages?
Razor Pages can use model binding, but care must be taken to ensure that the value of the form element’s name attribute matches the name of the handler method parameter, which may not be the case if the asp-for attribute has been used to select a nested property. To ensure the names match, the name attribute can be defined explicitly like so:
The tag helper would have set the name attribute of the input element to Product.Name, which prevents the model binder from matching the values. Explicitly setting the name attribute overrides the tag helper and ensures the model binding process works correctly.
What happens if data values cannot be located?
Model binding is a best-effort feature, which means the model binder will try to get values for method parameters but will still invoke the method if data values cannot be located.
An exception isn’t reported by the model binding system. Instead, it is reported when the Entity Framework Core query is executed.
When there is no value available for model binding, the action method tries to query the database with an id of zero. There is no such object, which causes sn error when Entity Framework Core tries to process the result.
How do you deal with the issue if no data value is located?
Applications must be written to cope with default argument values, which can be done in several ways. You can add fallback values to the routing URL patterns used by controllers (as shown in Chapter 21) or pages (as shown in Chapter 23). You can assign default values when defining the parameter in the action or page handler method, or you can simply write methods that accommodate the default values without causing an like the following:
…
public async Task Index(long id) {
ViewBag.Categories = new SelectList(context.Categories, “CategoryId”, “Name”);
return View(“Form”, await context.Products.Include(p => p.Category)
.Include(p => p.Supplier).FirstOrDefaultAsync(p => p.ProductId == id));
}
…
The Entity Framework Core FirstOrDefaultAsync method will return null if there is no matching object in the database and won’t attempt to load related data. The tag helpers cope with null values and display empty fields,
How to differentiate between a missing value and any value provided by the user?
Some applications need to differentiate between a missing value and any value provided by the user. In these situations, a nullable parameter type can be used.
…
public async Task Index(long? id) {
ViewBag.Categories = new SelectList(context.Categories, “CategoryId”, “Name”);
return View(“Form”, await context.Products.Include(p => p.Category)
.Include(p => p.Supplier)
.FirstOrDefaultAsync(p => id == null || p.ProductId == id));
}
…
The id parameter will be null only if the request doesn’t contain a suitable value, which allows the expression passed to the FirstOrDefaultAsync method to default to the first object in the database when there is no value and to query for any other value.
Example:
http://localhost:5000/controllers/Form and http://localhost:5000/controllers/Form/index/0. The first URL contains no id value, so the first object in the database is selected. The second URL provides an id value of zero, which doesn’t correspond to any object in the database.
What are complex types?
any type that cannot be parsed from a single string value