Model Binding is the process of automatically mapping HTTP request data (eg: from input,
query string etc), to action method parameters or .NET object. It simplifies development by
abstracting the parsing HTTP data and letting to focus on BL. Eg; when a client sends data
in a request, the model binding automatically translate data into strongly typed object.
There are some resources that model binding use for:
Query String [FromQuery]: Used when data is sent via URL query string
(?name=John). Ideal
for simple types and filters, paginations in GET request.
Route Data [FromRoute]: Used to bind parameters from URL path
(/users/5).
Useful for ID and
values defined in route.
Form Data [FromForm]: Used for APIs that receive JSON payloads, like
POST/PUT.
files
Request Body [FromBody]: Used when raw body content we have, usually JSON
and maps it
complex types. Only one parameter is allowed per action, means we can not have more than one
parameter with FromBody like:
PostUser([FromBody] UserDto user, [FromBody]
AddressDto
address) Because if we send multiple parameter and during deserialize into objects,
the framework wouldn't know how to split them.
Headers [FromHeader]: Used for authentication token, and binds value from
Http header.
Default Binding (No Attribute): When no attribute (like above) is used ,
then frameworks
choose the source automatically based on parameter type. So, simple types like
int, string, bool, datetime bind from Route, QueryString, Form source.
Eg1: public
IActionResult GetUser(int id){//your code} Comes from /GetUser?id=7. Complex types
(classes) bind from request body,
Eg2:
public IActionResult GetUser(UserDto
user){//your
code} except JSON in the body like {"name":{Firuza}}
Normally, .NET takes data from the request and automatically fills our object, this process is called model binding. But if the data comes from multiple places like part of URL and part of body or comes from in a weird format, then the default binding may not work, in this situation Custom Model Binding lets us write our own rules for how to handle request data and turn it our object. We use it to bind data from non-standart request format; to combine multiple input sources into one model. How to use?
Eg: Lets say user sends date in format "dd-MM-yyyy" from the query string (like ?dob=26-06-2025), but by default we expects something "yyyy-mm-dd" like 2025-06-26. So if user send 26-06-2025 then model binding fails. To solve it we override the excepted format, we convert it into valid DateTime object, even if system except yyyy-mm-dd, code will work without client needing change their format. (see below image)
After model binding occurs, to track the validity of model binding, like to check if
incoming data is valid according to our model we use it. Used in controller actions and will
contain the errors.
Firstly lets see what are validations- it refers to the process used to ensure that
data
entered by user is correct before it stored. Key concepts & Types of Validation:
Data Annotations: are provide validation on client-side and server-side by using attributes directly on model properties. Simplifies code logic, ensure validation applied same for client and server. It has a range of built-in attributes like below and besides we can also create Custom Data Annotations.
Are used to encapsulate and transfer data between layers of application, like between UI layer, Service layer and Data Access Layer. Ensure clean separation by reducing unnecessary data from external system like APIs. So we can say it is a simple container without BS logic. To use it, we defining domain model which represent how data is reftected in DB, then creating a DTO for exclude sensitive fields, and then we map data manually (to simplify it we use AutoMapper later). Generally we create seperate DTO for different operations, like for create operation CustomerCreateDTO (write/update) - exclude sensitive data like passwords, for fetch operation CustomerGetDTO(read-only) - include sensitive data if needed like Price,AdminNote.
As earlier article I mentioned that there are different layers for
application, where each layer has its own representation of data. DAL, BL, Application
Layer, and converting data between models and Dto especially if application grows can be
problem. So, Automapper is an open-source object-to-object mapping library , used to convert
object of one type to another type automatically. Commonly used to simplifying conversion
between different layers of application, instead manual conversion code between DTO and
domain models or vice versa. Lets start what is Model?- It indicate data structure in DB and
map data directly to DB table. And every field needs to store data.
Eg: we have Customer model and customerDTO, we can define mapping between these objects. We
use TSoruce- object we are mapping From, TDestination-we are mapping To. We use
ForMember() and MapFrom()
to customize how one property mapped to another when property names don't match or need
changes, Eg; we map 'Name' as 'UserName' or vice vers (see MappingProfile.cs)
Map configuration:
- Install NuGet packages in the project;
(AutoMapper,DependencyInjection);
- Creating AutoMapper Profiles between two models;
- Use the AutoMapper instance (IMapper) to perform mapping;
CreateMap<TSource,TDestination>(); used to define mapping for all instances. For
all
Create operations, all Get operations.
CreateMap<CustomerDTO, Customer>(); // Mapping for create operations
CreateMap<Customer, CustomerDTO>(); // Mapping for get/retrieve operations
Mapping From DTO to Domain:
var customer = mapper.Map<Customer>(customerDTO); In here CustomerDTO
(TSource) is data we get from external system like user input, and Customer
(TDestination) is domain model we want to populate from DTO, used in create
operation. AutoMapper generates a new instance of Customer from customerDTO,
customerDTO provides all the necessary data.
Mapping from Domain to DTO:
var customerDTO = mapper.Map<CustomerDTO>(Customer); means mapping from
domain model to DTO. It is used when getting data from DB, and mapping
it to DTO to return back to client. The Customer object provides the
source data, and AutoMapper creates a new instance of CustomerDTO for
you.
Using Map<TSource, TDestination>(sourceObj)- is optional syntax, if we
want to use CreateMap<TS,TD> syntax also for Map<TD>(TS), then we
use instance object like:
Map<Customer,CustomerDTO>(customers); and this same with Map
<CustomerDTO>(Customer);
Update Existing Object:
var updatedCustomer = _mapper.Map(updateDto,
Customer); means we are working with existing
Customer object, and applying changes from DTO to
existing object. It wont create new Customer object,
update existing one.
Below image, without AutoMapper PROBLEM : we will get the output as expected. But if the data ( e.g . properties) are increased then mapping is repeated between the source and the destination