Model Binding in ASP.NET Core MVC

Model Binding in ASP.NET Core MVC

In this article, I will discuss Model Binding in ASP.NET Core MVC Applications with Examples. Please read our previous article discussing Tag Helpers in ASP.NET Core MVC. At the end of this article, you will understand the following pointers:

  1. What is Model Binding in ASP.NET Core MVC?
  2. Example Without Using Model Binding in ASP.NET Core MVC
  3. What is IFormCollection in ASP.NET Core MVC?
  4. What are the Limitations of IFormCollection in ASP.NET Core MVC?
  5. Example Using Model Binding in ASP.NET Core MVC
  6. How Does Model Binding Work in ASP.NET Core MVC?
  7. Different Model Binding Techniques in ASP.NET Core MVC
  8. Why Model Binding in ASP.NET Core MVC?
  9. Built-in Model Binders in ASP.NET Core MVC
What is Model Binding in ASP.NET Core MVC?

Model Binding in ASP.NET Core MVC is the process by which HTTP request data (like form data, route data, query strings, and headers) is automatically mapped to action method parameters or model properties. The action method parameters can be simple types like integers, strings, etc., or complex types such as Student, Order, Product, etc.

When a request is made to an MVC application, the data sent from the client is often in a format (such as query strings, form data, and route data) that needs to be converted into .NET objects. Model Binding simplifies this process by handling this conversion automatically, allowing developers to work with strongly typed models instead of raw HTTP data.

Example Without Using Model Binding in ASP.NET Core MVC

Let us first understand one example without using Model Binding, i.e., how to capture request data in an ASP.NET Core MVC Application without Model Binding. In ASP.NET Core MVC, we can use the IFormCollection interface to extract data manually from HTTP requests.

What is IFormCollection in ASP.NET Core MVC?

IFormCollection is an interface in ASP.NET Core MVC that represents a collection of form data submitted in an HTTP POST and PUT requests. It provides access to form fields and their values, allowing developers to work with the form data directly. The form fields are stored as key-value pairs, where the key is the field name, and the value is the field value(s). It provides the following methods and properties to retrieve form values:

  • Keys – Retrieves all the keys in the form collection. It will hold all the form field names.
  • ContainsKey(key) – Determines if a specific key is present in the form collection.
  • TryGetValue(key, out value) – Attempts to get the value associated with a specific key.

The IFormCollection can be used as a parameter in an action method to access form data directly without binding each form field to a model property. 

Example using IFormCollection in ASP.NET Core MVC

Let’s understand how to use IFormCollection to fetch data from HTTP Requests. First, create a new ASP.NET Core MVC Application named ModelBindingDemo. Then, modify the HomeController as follows. As you can see, we are using IFormCollection as a parameter to capture the form data using the controller action method.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;

namespace ModelBindingDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            // Return the default view associated with this action.
            return View();
        }

        // Indicate that this action method handles POST requests.
        [HttpPost]
        // Define the SubmitForm action method, accepting an IFormCollection object as a parameter.
        public IActionResult SubmitForm(IFormCollection form)
        {
            // Use Keys to get the collection of form keys
            var keys = form.Keys; // Retrieve all the keys (form field names) from the form collection and store them in the 'keys' variable.

            // Check if a specific key is present using ContainsKey
            // Check if the form contains the keys "UserName" and "UserEmail".
            if (form.ContainsKey("UserName") && form.ContainsKey("UserEmail"))
            {
                // You Can access the Data using String Indexer
                // var UserName = form["UserName"].ToString();
                // var UserEmail = form["UserEmail"].ToString();

                // Retrieve the value associated with a key using TryGetValue
                if (form.TryGetValue("UserName", out StringValues userName) && form.TryGetValue("UserEmail", out StringValues userEmail))
                // Try to retrieve the value for "UserName" and "UserEmail" from the form.
                // If both keys exist, their values are stored in 'userName' and 'userEmail' respectively.
                {
                    // If both values were successfully retrieved, store a success message in ViewBag with the user's name and email.
                    ViewBag.Message = $"User Created: UserName: {userName}, UserEmail: {userEmail}";
                }
                else // If either "UserName" or "UserEmail" was not found in the form data,
                {
                    // store an error message in ViewBag indicating that the required form data was not found.
                    ViewBag.Message = "UserName or UserEmail not found in the form data.";
                    
                }
            }
            else // If either the "UserName" or "UserEmail" keys are not present in the form,
            {
                // store a message in ViewBag indicating that the form is missing one or both required keys.
                ViewBag.Message = "Form does not contain UserName or UserEmail.";
            }

            //Return to the Index View
            return View("Index");
        }
    }
}
Modifying the Index View:

Next, modify the Index.cshtml view as follows:

@{
    ViewData["Title"] = "Home Page";
}

<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card">
                <div class="card-header bg-primary text-white">
                    <h4 class="mb-0">Submit Your Details</h4>
                </div>
                <div class="card-body">
                    @if (!string.IsNullOrEmpty(ViewBag.Message))
                    {
                        <div class="alert alert-success">
                            @ViewBag.Message
                        </div>
                    }
                    <form method="post" asp-controller="Home" asp-action="SubmitForm">
                        <div class="form-group mt-2">
                            <label for="UserName">Name:</label>
                            <input type="text" class="form-control" id="UserName" name="UserName" placeholder="Enter your name" />
                        </div>
                        <div class="form-group  mt-2">
                            <label for="UserEmail">Email:</label>
                            <input type="email" class="form-control" id="UserEmail" name="UserEmail" placeholder="Enter your email" />
                        </div>
                        <div class="form-group mt-2">
                            <button type="submit" class="btn btn-primary btn-block">Submit</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

Now, run the application and access the Index page. It should open on the following page. Provide the User Name and Email address and click on the Submit button as shown in the below image:

Example using IFormCollection in ASP.NET Core MVC

Once you click on the submit button, you should get the following message:

Example Without Using Model Binding in ASP.NET Core MVC

What are the Limitations of IFormCollection in ASP.NET Core MVC?

The following are the limitations of IFormCollection in ASP.NET Core MVC:

  • Limited to Form Data Only: IFormCollection can only handle form data submitted via POST requests. It cannot be used to access data from other sources, such as query strings, route data, or JSON bodies.
  • No Strong Typing: The data in IFormCollection is accessed via key-value pairs, meaning you lose the benefits of strong typing. You need to manually cast or convert the data to the desired types, which can lead to errors if the conversion fails.
  • Lack of Validation: IFormCollection does not automatically validate the data. You must manually check for required fields, data types, and other validation rules, increasing the risk of errors.
  • Scalability Issues: For large and complex forms, manually extracting and processing each form field using IFormCollection can become difficult, time-consuming, and less maintainable. As the number of fields increases, the code becomes more error-prone and harder to manage.
  • Manual Data Handling: With IFormCollection, we need to handle data conversions, null checks, and errors manually. Model binding handles this automatically, making our code cleaner and easier to maintain.
  • No Built-in Security Checks: IFormCollection does not inherently provide security features like anti-forgery token validation, which are more easily integrated when using model binding with view models.
Example Using Model Binding in ASP.NET Core MVC

Let’s see how we can rewrite the previous example using Model Binding in ASP.NET Core MVC. First, let’s create a model to hold the UserName and UserEmail data, which will be submitted from the form. So, create a class file with the name User.cs within the Models folder and copy and paste the following code. We are making the UserName and Email field mandatory using the [Required] data annotation attribute.

using System.ComponentModel.DataAnnotations;

namespace ModelBindingDemo.Models
{
    public class User
    {
        [Required(ErrorMessage = "Name is required.")]
        public string UserName { get; set; }

        [Required(ErrorMessage = "Email is required.")]
        [EmailAddress(ErrorMessage = "Invalid Email Address.")]
        public string UserEmail { get; set; }
    }
}

Next, modify the Index.cshtml view as follows. As you can see, the User is now the model for the following view. Here, we are using Tag Helpers to Create the form and input elements.

@model User

@{
    ViewData["Title"] = "Home Page";
}

<div class="container mt-5">
    <div class="row justify-content-center">
        <div class="col-md-6">
            <div class="card">
                <div class="card-header bg-primary text-white">
                    <h4 class="mb-0">Submit Your Details</h4>
                </div>
                <div class="card-body">
                    @if (!string.IsNullOrEmpty(ViewBag.Message))
                    {
                        <div class="alert alert-success">
                            @ViewBag.Message
                        </div>
                    }
                    <form method="post" asp-controller="Home" asp-action="SubmitForm">
                        <div class="form-group mt-2">
                            <label asp-for="UserName"></label>
                            <input asp-for="UserName" class="form-control" placeholder="Enter your name" />
                            <span asp-validation-for="UserName" class="text-danger"></span>
                        </div>
                        <div class="form-group mt-2">
                            <label asp-for="UserEmail"></label>
                            <input asp-for="UserEmail" class="form-control" placeholder="Enter your email" />
                            <span asp-validation-for="UserEmail" class="text-danger"></span>
                        </div>
                        <div class="form-group mt-2">
                            <button type="submit" class="btn btn-primary btn-block">Submit</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>

Next, modify the HomeController as follows:

using Microsoft.AspNetCore.Mvc;
using ModelBindingDemo.Models;

namespace ModelBindingDemo.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public IActionResult SubmitForm(User user)
        {
            if (user != null)
            {
                if (ModelState.IsValid)
                {
                    // Store success message in ViewBag
                    ViewBag.Message = $"User Created: UserName: {user.UserName}, UserEmail: {user.UserEmail}";

                    // Optionally, you could clear the model to reset the form if needed
                    ModelState.Clear();

                    return View("Index");
                }
            }
            return View("Index", user); // Return the Index view with the user model
        }
    }
}

Now, run the application, and it should work as expected. Once you access the Index Page, the following form will be displayed: Provide the User Name and Valid Email address and click on the Submit button, as shown in the image below.

Example using IFormCollection in ASP.NET Core MVC

Once you click on the Submit button, you should see the following message:

How Does Model Binding Work in ASP.NET Core MVC

Now, click the Submit button without Providing the User Name and Email address. You should see the Validation Error messages as shown in the image below:

Different Model Binding Techniques in ASP.NET Core MVC

I hope you understand the Basic Concepts of Model Binding in ASP.NET Core MVC. Let’s proceed to understand how Model Binding Works Behind the Scenes in ASP.NET Core MVC.

How Does Model Binding Work in ASP.NET Core MVC?

Model binding in ASP.NET Core MVC works by inspecting the parameters of the action method and then looking through the sources of the request data (like form values, route data, query string parameters, etc.) to find matches. It uses various binders for different types of data. For example, simple types like int and string can be bound from route data or the query string, while complex types are typically bound from form values or JSON body data. The model binder is also responsible for validation based on data annotations or custom validators applied to the model properties. So, Model Binding in ASP.NET Core MVC works as follows:

  • Determine the Binding Source: ASP.NET Core MVC first determines the source of the data based on the action method’s parameters. Depending on the source attribute (e.g., [FromQuery], [FromBody], [FromRoute]), it knows where to look, such as in the query string, the request body, or the route data.
  • Fetch Data from the Source: The framework retrieves the data from the identified sources.
  • Match and Convert Data: The model binder matches the data with the action method parameters or model properties. If the names match, the data is converted to the appropriate type.
  • Bind Data to Parameters or Models: The converted data is then bound to the parameters or properties.
  • Handle Errors: If binding fails (e.g., due to a type mismatch), the model binder may add errors to the ModelState dictionary, which can be used to handle validation errors in the application.
Different Model Binding Techniques in ASP.NET Core MVC:

You can configure model binding using various attributes that control how data is extracted from the request. Some common attributes include:

  • [FromBody]: This attribute tells ASP.NET Core to bind data from the request body to the parameter of an action method. It’s commonly used when complex types like objects or collections, typically in JSON or XML format, are expected in the body of a POST request. It is commonly used in Restful Services.
  • [FromForm]: The [FromForm] attribute binds data from HTML form fields to action method parameters. It’s suitable for handling form submissions via HTTP POST or PUT requests and is commonly used in ASP.NET Core MVC Applications.
  • [FromQuery]: Binds data from the query string of the URL to action parameters. The [FromQuery] attribute binds data from the URL’s query string to action method parameters. It’s often used in HTTP GET requests where data is passed in the URL as Query String Parameters.
  • [FromRoute]: The [FromRoute] attribute binds data from route parameters to action method parameters. Route parameters are placeholders in the URL defined in your route templates.
  • [FromHeader]: It helps extract custom or standard headers, such as Authorization, User-Agent, etc. This is often used in Restful Services.

Note: We will discuss these model-binding techniques in detail in our next article. 

Why Model Binding in ASP.NET Core MVC?

Model Binding automatically maps request data into .NET types and handles complexities like data type conversions, validation, and null-checking. This allows developers to focus on business logic and controller functionality rather than manually parsing and validating HTTP request data. Model Binding in ASP.NET Core MVC is essential because:

  • Simplifies Data Handling: It abstracts the complexity of fetching and converting HTTP request data into .NET objects, making code cleaner and easier to maintain.
  • Automatic Validation: Combined with Data Annotations, it allows for automatic data validation before it reaches the action method, reducing the amount of manual validation code.
  • Consistency: It ensures that data is consistently converted and bound to parameters, reducing the likelihood of errors.
  • Flexibility: Different sources of data (route, query string, form data, etc.) can be bound easily without needing custom code for each source.

Built-in Model Binders in ASP.NET Core MVC:

ASP.NET Core MVC includes several built-in model binders to handle different types of data. They are as follows:

  • Simple Type Model Binder: Binds simple types like integers, strings, booleans, etc.
  • Complex Type Model Binder: Binds complex types (e.g., classes) by recursively binding their properties.
  • Array and Collection Model Binder: Binds arrays and collections like List<T>, IEnumerable<T>, etc.
  • Dictionary Model Binder: Binds dictionary types like Dictionary<TKey, TValue>.
  • DateTime Model Binder: Specifically handles binding DateTime types.
  • FormFile Model Binder: Binds uploaded files using IFormFile.
  • FormCollection Model Binder: Binds form data using IFormCollection.
  • Complex Object Model Binder: Binds complex objects that may contain other complex types or collections.
  • Enum Model Binder: Binds enum values.

The default order of these providers is designed to handle most typical scenarios effectively. This order determines how the framework binds incoming data to action method parameters or properties of complex types. To see the same, please add the following code to the Program.cs class file. It will display the default order of all the registered Model Binders in the ASP.NET Core Application. Once you run the application, check the Console Window.

builder.Services.AddControllersWithViews(options =>
{
    foreach (var provider in options.ModelBinderProviders)
    {
        Console.WriteLine(provider.GetType().ToString());
    }
});

To control the priority or decide which source to use in model binding, specify the appropriate attribute for each action method parameter. For example, suppose you have an action method that could receive a userId either from the query string or as a JSON payload. In that case, you can decorate the parameter using either [FromQuery] or [FromBody] as follows:

public IActionResult GetUser([FromQuery] int userId)
{
    // Uses userId from the query string
}

// OR

public IActionResult UpdateUser([FromBody] User user)
{
    // User object is expected as a JSON body which includes userId
}

In the next article, I will discuss Using FromForm to Perform Model Binding in ASP.NET Core MVC with Examples. In this article, I try to explain the basic concepts of Model Binding. I hope you enjoy this article on Model Binding in ASP.NET Core MVC.

9 thoughts on “Model Binding in ASP.NET Core MVC”

  1. Mahinur Rahaman Hridoy

    Don’t we have the opportunity to learn a little more?
    I’m from Bangladesh.
    Thanks a lot.. This is the best tutorial site for me. I have never seen such a website

  2. Its a greattt tutorial …but so many things also we are expecting ..Please provide the continuation of this technology

Leave a Reply

Your email address will not be published. Required fields are marked *