Model Binding in ASP.NET Core from Beginners to Advanced

Model Binding in ASP.NET Core from Beginners to Advanced

Models contain or represent the data that Users work with. Models are basically C# classes that are filled with data from some database by the Controllers. The 2 most important topics about Models are:

1. Model Binding – a process of extracting data from HTTP request and providing them to the action method’s arguments.

2. Model Validation – a process to validate the Model properties so that invalid entries are not entered in the database.

In this tutorial you will learn about Model Binding and Model Validation process in details.

Create the Example Project

For this tutorial use ASP.NET Core Web Application (.NET Core) template to create a new Empty Project and name it ‘ModelBindingValidation’. Remember to select the framework as .NET Core and version as ASP.NET Core 2.0.

Models & Repository

Create ‘Models’ folder in your application’s root and add an ‘Employee.cs’ class to it, use it to define classes and enum as given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ModelBindingValidation.Models
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime DOB { get; set; }
        public Address HomeAddress { get; set; }
        public Role Role { get; set; }
    }
    public class Address
    {
        public string HouseNumer { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }

    public enum Role
    {
        Admin,
        Designer,
        Manager
    }
}

Next, create a class file called ‘Repository.cs’ to the Models folder and define an Interface and implementation class as shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ModelBindingValidation.Models
{
    public interface IRepository
    {
        IEnumerable<Employee> Employee { get; }
        Employee this[int id] { get; set; }
    }

    public class EmployeeRepository : IRepository
    {
        private Dictionary<int, Employee> employee = new Dictionary<int, Employee>
        {
            [1] = new Employee
            {
                Id = 1,
                Name = "John",
                DOB = new DateTime(1980, 12, 25),
                Role = Role.Admin
            },
            [2] = new Employee
            {
                Id = 2,
                Name = "Michael",
                DOB = new DateTime(1981, 5, 13),
                Role = Role.Designer
            },
            [3] = new Employee
            {
                Id = 3,
                Name = "Rachael",
                DOB = new DateTime(1982, 11, 25),
                Role = Role.Designer
            },
            [4] = new Employee
            {
                Id = 4,
                Name = "Anna",
                DOB = new DateTime(1983, 1, 20),
                Role = Role.Manager
            }
        };

        public IEnumerable<Employee> Employee => employee.Values;

        public Employee this[int id]
        {
            get
            {
                return employee.ContainsKey(id) ? employee[id] : null;
            }
            set
            {
                employee[id] = value;
            }
        }
    }
}

Controllers & Views

Create the ‘Controllers’ folder in the root of your application and add a controller by name ‘Home’ Controller to it. The controller depends upon the dependency injection feature to receive the EmployeeRepository.cs class.

The HomeController.cs file’s code is shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ModelBindingValidation.Models;

namespace ModelBindingValidation.Controllers
{
    public class HomeController : Controller
    {
        private IRepository repository;
        public HomeController(IRepository repo)
        {
            repository = repo;
        }
        public ViewResult Index(int id)
        {
            return View(repository[id]);
        }
    }
}

Create ‘Views/Home’ folder and add Index.cshtml file for the Index View. The View receives a model of type ‘Employee’ and binds some of the model properties inside a table.

Add the following code to it:

@model Employee
@{
    Layout = "_Layout";
    ViewData["Title"] = "Index";
}

<h2>Employee</h2>

<table class="table table-sm table-bordered table-striped">
    <tr><th>Id:</th><td>@Model.Id</td></tr>
    <tr><th>Name:</th><td>@Model.Name</td></tr>
    <tr><th>Date of Birth:</th><td>@Model.DOB.ToShortDateString()</td></tr>
    <tr><th>Role:</th><td>@Model.Role</td></tr>
</table>

Next add a layout, i.e. a view by name ‘_Layout.cshtml’, inside the ‘View/Shared’ folder. Add the following code to it:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link asp-href-include="/lib/bootstrap/dist/**/*.min.css" rel="stylesheet" />
    @RenderSection("scripts", false)
</head>
<body>
    <div class="m-1 p-1">
        @RenderBody()
    </div>
</body>
</html>

The layout reference Bootstrap CSS file and also contains an optional ‘scripts’ section.

Next add View Imports file, i.e. a view by name ‘_ViewImports.cshtml’, for importing namespaces and built-in tag helpers.

Add the following code to it:

@using ModelBindingValidation.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

The Views of this example needs bootstrap framework for providing styles to element. Bootstrap can be added through bower.json file.

Create bower.json file on the root of the application and add the bootstrap package as shown below:

{
  "name": "asp.net",
  "private": true,
  "dependencies": {
    "bootstrap": "v4.1.3"
  }
}

On saving the bower.json file the Bootstrap package will automatically download to your ‘wwwroot/lib/bootstrap’ folder.

Configure the Project

Use the ‘Startup.cs’ class to do the configuration of your project. Here I have I have created a service for the ‘EmployeeRepository’ class so that the controller can gain access to this class using dependency injection.

The Startup class code is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using ModelBindingValidation.Models;

namespace ModelBindingValidation
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IRepository,EmployeeRepository>();
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseStatusCodePages();
            app.UseDeveloperExceptionPage();
            app.UseStaticFiles();
            app.UseMvc(routes => {
                routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

What is Model Binding

Model Binding is a process done by ASP.NET Core MVC to extract data from HTTP requests and provide them to Action method’s arguments. Let us see the first example of Model Binding process in this example project.

So run your project and go to URL – ‘/Home/Index/1’. You will see the record of the employee having id as 1 gets displayed on the browser. This is shown in the image below:

model binding employee record with id 1

Here Model Binding feature is used to because the URL I requested contained the value of ‘Id’ property (given in the 3rd segment of the URL which is 1) of the Employee object that I want to view, like this:

/Home/Index/1

According to the routes of the project you know that the 3rd segment of the URL is for Id segment:

routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");

Now, you can see that the Index Action Method is having the ‘Id’ argument.

public ViewResult Index(int id)
{
    return View(repository[id]);
}

So Core MVC extracts the id value from the URL (which is 1) and provides it to the id argument of the action method.

The Index Action method in turn gets the employee id 1 record, from the repository, and returns it to the View for displaying purpose.

The Core MVC looks for action method’s argument (here int id) value in the following 3 places:

1. Form data values
2. Routing variables
3. Query strings

Each place is inspected starting from Form data values, then Routing variable and finally with Query String.
In this case the Core MVC did not found the value of Id in Form data values, it then checked the routing variables where there is routing segment called id. So the search ended here, without the query string searched for the value.

The order of search is important. If you open the URL – ‘/Home/Index/2?id=1’ then you will see the Employee id 2 record is displayed, as shown in the image below:

model binding employee record with id 2

The reason why Employee id 2 is displayed is because the Core MVC finds the value of id (i.e 2) in the routing variable before the value of id (i.e. 1) in the query sting so value of 2 is provided to the action method’s argument.

If you open the URL – ‘/Home/Index?id=3’ then the Employee id 3 is displayed as shown in the image below:

model binding employee record with id 3

In this case the Core MVC did not find the id value in the URL segment so it moves to search the query string where it found the id value of 3.

Default Binding Values

You may wonder what will happen if Core MVC did not find the values of the action method’s argument in any of the three locations – Form data values, Routing variables & Query strings. In that case it will provide the default values based on the type of the action method’s argument:

  • 0 for int
  • “” for string
  • 01-01-0001 00:00:00 for DateTime
  • 0 for float

In order to test the default binding value change the Index action method to as shown in the below code:

public ViewResult Index(int id)
{
    if (id == 0)
        id = 1;
    return View(repository[Convert.ToInt32(id)]);
}

Now put a breakpoint on the action method (breakpoint can be put a line where your cursor is and pressing the F9 key).

Run your application and go to URL – ‘/Home/Index’. When your breakpoint touches, put the mouse pointer over ‘id’ and you will see the value 0, as shown by the image given below:

Default Binding Values

Since there is no employee of id 0 therefore I am checking the value of id for 0, and if it is so then making it 1. This will help me to prevent runtime errors.

Note that the default values of nullable types are null.

Change the Index action to have nullable int argument as shown in the code below:

public ViewResult Index(int? id)
{
    if (id == null)
        id = 1;
    return View(repository[Convert.ToInt32(id)]);
}

Run your application and go to URL – ‘/Home/Index’ and you will find the id value this time is null.

Model Binding for Simple Types

When Binding Simple Types, Core MVC convert the values into the types of action method’s arguments. The Simple Types are – string, int, bool, float, datetime, decimal, etc.

Let us see it with an example of the Index Action which is shown below:

public ViewResult Index(int? id)
{
    if (id == null)
        id = 1;
    return View(repository[Convert.ToInt32(id)]);
}

The id argument of the Index action method is an int, therefore Core MVC automatically parses the value of the id segment variable into an int value.

Case 1: For URL – /Home/Index/1

When you request URL which contains id value as int like – ‘/Home/Index/1’, then MVC will successfully parse value of 1 to int and so the ‘id’ argument of the action method receives 1.

Case 2: For URL – /Home/Index/John

Now if you request a URL where the id value is a string like – ‘/Home/Index/John’, then in this case Core MVC will try to parse ‘John’ to int value. It fails to parse in this case, so ‘id’ argument of the action method receives null value.

Model Binding for Complex Types

When the argument of the action method is a complex type like a class object then Model Binding process gets all the public properties of the complex type and performs the binding of each of them.

The Model binding process looks for the values of these public properties in the following 3 places:

1. Form data values
2. Routing variables
3. Query strings

Let me demonstrate how this works by an example.

Add Create HTTP GET and HTTP POST Action methods on the Home Controller. The code for these 2 actions is given below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ModelBindingValidation.Models;

namespace ModelBindingValidation.Controllers
{
    public class HomeController : Controller
    {
        private IRepository repository;
        public HomeController(IRepository repo)
        {
            repository = repo;
        }

        public ViewResult Index(int? id)
        {
            if (id == null)
                id = 1;
            return View(repository[Convert.ToInt32(id)]);
        }

        public ViewResult Create() => View();

        [HttpPost]
        public ViewResult Create(Employee model) => View("Index", model);
    }
}

The HTTP GET version of the Create Action Method selects the default View and did not pass any model to it.

The HTTP POST version of the Create Action Method has the argument of ‘Employee’ type (which is a complex type).

The Model Binding feature will extract the value of the Employee public properties from the HTTP request and provide it to the action method argument. The action method then passes the Employee object as Model to the default view.

Next create the ‘Create’ View inside the ‘/Views/Home’ folder. Create View code is given below:

@model Employee
@{
    Layout = "_Layout";
    ViewData["Title"] = "Create Employee";
}

<h2>Create Employee</h2>

<form asp-action="Create" method="post">
    <div class="form-group">
        <label asp-for="Id"></label>
        <input asp-for="Id" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="Name"></label>
        <input asp-for="Name" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="DOB"></label>
        <input asp-for="DOB" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="Role"></label>
        <select asp-for="Role" class="form-control"
                asp-items="@new SelectList(Enum.GetNames(typeof(Role)))"></select>
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

The View contains a form and binds some of the public properties of the Employee model. When the form is submitted these values are posted to the HTTP POST version of the Create Action method.

The create action requires an Employee objects in its parameter. So the Model Binding process looks for the value of each public property (Id, Name, DOB, Role) of the Employee object in the form data.

Since the form data contains these values, which is set up using asp-for tag helper on input elements, therefore the Model Binding process put these values to the properties of Employee object given in the argument of the create action method. This way the Employee object of the create action gets the value from the View.

The Post version of the Create Action invokes the Index View and passes the Employee object as a Model to it.
Run your application and go to URL – ‘/Home/Create’. Fill all form values and click the submit button and you will
see the filled values getting displayed on the browser.

The below 2 images shows the filling and submitting of the form:

model binding complex type 1

model binding complex type 2

Model Binding for Complex Type that contains another Complex Type

The ‘Employee’ class contains a public ‘HomeAddress’ property which is of ‘Address’ class type.

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime DOB { get; set; }
    public Address HomeAddress { get; set; }
    public Role Role { get; set; }
}

public class Address
{
    public string HouseNumber { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }
}

When binding the HomeAddress property the Model Binding process does the same thing like before:

1. Finds out all the public properties of the HomeAddress (i.e HouseNumber, Street, City, PostalCode, Country).
2. Search the values of these public properties in the form data.

So to make the Model Binding process to extract ‘HouseNumber’ property values you will use asp-for=”HomeAddress.HouseNumber” in the view.

In the below updated code of the Create View I am binding all the five properties of the HomeAddress too:

@model Employee
@{
    Layout = "_Layout";
    ViewData["Title"] = "Create Employee";
}

<h2>Create Employee</h2>

<form asp-action="Create" method="post">
    <div class="form-group">
        <label asp-for="Id"></label>
        <input asp-for="Id" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="Name"></label>
        <input asp-for="Name" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="DOB"></label>
        <input asp-for="DOB" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="Role"></label>
        <select asp-for="Role" class="form-control"
                asp-items="@new SelectList(Enum.GetNames(typeof(Role)))"></select>
    </div>
    <div class="form-group">
        <label asp-for="HomeAddress.HouseNumber"></label>
        <input asp-for="HomeAddress.HouseNumber" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="HomeAddress.City"></label>
        <input asp-for="HomeAddress.City" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="HomeAddress.Street"></label>
        <input asp-for="HomeAddress.Street" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="HomeAddress.PostalCode"></label>
        <input asp-for="HomeAddress.PostalCode" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="HomeAddress.Country"></label>
        <input asp-for="HomeAddress.Country" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

Next edit the Index View to display the properties of the ‘HomeAddress’ as shown by the code below:

@model Employee
@{
    Layout = "_Layout";
    ViewData["Title"] = "Index";
}

<h2>Employee</h2>

<table class="table table-sm table-bordered table-striped">
    <tr><th>Id:</th><td>@Model.Id</td></tr>
    <tr><th>Name:</th><td>@Model.Name</td></tr>
    <tr><th>Date of Birth:</th><td>@Model.DOB.ToShortDateString()</td></tr>
    <tr><th>Role:</th><td>@Model.Role</td></tr>
    <tr><th>House No:</th><td>@Model.HomeAddress?.HouseNumber</td></tr>
    <tr><th>Street:</th><td>@Model.HomeAddress?.Street</td></tr>
    <tr><th>City:</th><td>@Model.HomeAddress?.City</td></tr>
    <tr><th>Postal Code:</th><td>@Model.HomeAddress?.PostalCode</td></tr>
    <tr><th>Country:</th><td>@Model.HomeAddress?.Country</td></tr>
</table>

Now run your application and go to URL – ‘/Home/Create’. Fill and submit the form and you will find the properties of the address type getting displayed, as shown in image below:

model binding complex type inside another complex type

Check the Source Code

Notice the source code generated for these HouseNumber property in the Create View, which is given below:

<label for="HomeAddress_HouseNumber">HouseNumber</label>
<input class="form-control" type="text" id="HomeAddress_HouseNumber" name="HomeAddress.HouseNumber" value="">

The input control gets the name – “HomeAddress.HouseNumber” while the id becomes – “HomeAddress_HouseNumber”.

You can also get the HouseNumber value in the action method by using Request.Form method as shown below:

[HttpPost]
public ViewResult Create(Employee model)
{
    string houseNo = Request.Form["HomeAddress.HouseNumber"];
    return View("Index", model);
}

Custom Binding using [Bind(Prefix)] Attribute

If you want to bind to a different object than what the Model Binding feature is looking. In that case you will have to use the [Bind(Prefix)] Attribute.

Let me give an example for this feature.

Create a new Model class ‘PersonAddress.cs’ with the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ModelBindingValidation.Models
{
    public class PersonAddress
    {
        public string City { get; set; }
        public string Country { get; set; }
    }
}

Next, add a new ‘DisplayPerson’ Action method to the HomeController class:

public ViewResult DisplayPerson(PersonAddress personAddress)
{
    return View(personAddress);
}

Next, change the value of ‘asp-action’ tag helper in the Create View to ‘DisplayPerson’ as shown below:

@model Employee
@{
    Layout = "_Layout";
    ViewData["Title"] = "Create Employee";
}

<h2>Create Employee</h2>

<form asp-action="DisplayPerson" method="post">
    ...
</form>

Now this means when you fill and submit the form in the Create View then the data will be posted to the ‘DisplayPerson’ View.

Finally create the ‘DisplayPerson’ View inside the ‘/Views/Home’ folder. This View has the model of type ‘PersonAddress’ and displays the City and Country given in the Model object.

The code for the ‘DisplayPerson’ View is given below:

@model PersonAddress
@{
    Layout = "_Layout";
    ViewData["Title"] = "Person";
}

<h2>Person</h2>

<table class="table table-sm table-bordered table-striped">
    <tr><th>City:</th><td>@Model.City</td></tr>
    <tr><th>Country:</th><td>@Model.Country</td></tr>
</table>

Now run your application and go to the URL – ‘/Home/Create’. Fill the complete form and click the submit button.

You will notice that the city and country values do not get displayed, see the image below:

empty data in model

The reason for this problem is that the Model Binding Process failed since name attribute of the ‘city and country’ input element have ‘HomeAddress’ prefix as shown by the below code.

<input class="form-control" type="text" id="HomeAddress_City" name="HomeAddress.City" value="">
<input class="form-control" type="text" id="HomeAddress_Country" name="HomeAddress.Country" value="">

The model binding process is not looking for these names (‘HomeAddress.City’ and ‘HomeAddress.Country’) in the form data. It is actually looking for only ‘City’ and ‘Country’ names in the form data.

To fix this problem apply the [Bind(Prefix)] Attribute in the action method’s parameter, which specifies the prefix that should be used during model binding.

Change the DisplayPerson action method code as shown below:

public ViewResult DisplayPerson([Bind(Prefix = nameof(Employee.HomeAddress))] PersonAddress personAddress)
{
    return View(personAddress);
}

Now once again submit the form and this time you will find the City and Country values being display on the browser, see the below image:

bind prefix attribute

Binding Selective Properties only

If there are some sensitive properties that should ‘not’ be bind then use the first argument of the Bind attribute.

Give the first argument all the names of the properties (in a comma-separated list) that should be only included in the model binding process. Therefore those prosperity names that are left will not be bind at all.

Change the ‘DisplayPerson’ action method code to:

public ViewResult DisplayPerson([Bind(nameof(PersonAddress.City), Prefix = nameof(Employee.HomeAddress))] PersonAddress personAddress)
{
    return View(personAddress);
}

Test it by filing and submitting the form, you will see that only the City value is displayed, see the image below:

only city gets bind

This is same as decorating the Country property of the PersonAddress class with the [BindNever] attribute, which tells Model Binding Process to not to bind this property ever:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace ModelBindingValidation.Models
{
    public class PersonAddress
    {
        public string City { get; set; }

        [BindNever]
        public string Country { get; set; }
    }
}

You can download the source code using the below link:

Download

Conclusion

Model binding is a well-designed bridge between the HTTP request and the C# action methods. It makes it easy for developers to work with data on forms (views), because POST and GET is automatically transferred into a data model you specify. Use this feature to make high class codes for your application.

Next topic – Advanced Model Binding Concepts in ASP.NET Core.

Share this article -

yogihosting

ABOUT THE AUTHOR

This article has been written by the Technical Staff of YogiHosting. Check out other articles on "ASP.NET Core, jQuery, EF Core, SEO, jQuery, HTML" and more.