Back to: ASP.NET Core Tutorials For Beginners and Professionals
Model Binding in ASP.NET Core MVC with Complex Type
In this article, I will discuss Model Binding in ASP.NET Core MVC with Complex Type. Please read our previous article discussing Model Binding FromRoute in ASP.NET Core MVC Application with Examples.
Model Binding in ASP.NET Core MVC with Complex Type
Model binding in ASP.NET Core automatically maps data from HTTP requests to action method parameters. The data sources can include route data, query string parameters, HTTP headers, body contents, and form values.
The Model Binding in ASP.NET Core MVC Application works with complex types like Customer, Student, Order, Product, etc. For complex types, which typically are classes with multiple properties, the model binder uses the names of the form fields, JSON properties, or other data sources to match up to the properties of the complex object. Let’s proceed to understand how to handle complex types in model binding within an ASP.NET Core MVC Application.
Creating Models:
First, create a model class to hold the posted form data. Here, we will create a form to hold the student data. So, add the following models:
Gender.cs:
Create a class file named Gender.cs and copy and paste the following code into it. This will be an enum that represents the gender-named constants.
namespace ModelBindingDemo.Models { public enum Gender { Male, Female } }
Branch.cs
Create a class file named Branch.cs and copy and paste the following code into it. This will be an enum that will represent the branches named constants.
namespace ModelBindingDemo.Models { public enum Branch { None, CSE, ETC, Mech } }
Student.cs
Create a class file named Student.cs and copy and paste the following code into it. This class will hold the student data and be our model.
namespace ModelBindingDemo.Models { public class Student { public int StudentId { get; set; } public string? Name { get; set; } public string? Email { get; set; } public string? Password { get; set; } public Branch? Branch { get; set; } public Gender? Gender { get; set; } public bool IsRegular { get; set; } public string? Address { get; set; } public DateTime DateOfBorth { get; set; } public List<string> Hobbies { get; set; } = new List<string>(); } }
Creating Student Controller:
Next, create an MVC Controller named StudentsController and copy and paste the following code into it.
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using ModelBindingDemo.Models; namespace ModelBindingDemo.Controllers { public class StudentsController : Controller { [HttpGet("Student/Create")] public ViewResult Create() { ViewBag.AllGenders = Enum.GetValues(typeof(Gender)).Cast<Gender>().ToList(); ViewBag.AllBranches = new List<SelectListItem>() { new SelectListItem { Text = "None", Value = "1" }, new SelectListItem { Text = "CSE", Value = "2" }, new SelectListItem { Text = "ETC", Value = "3" }, new SelectListItem { Text = "Mech", Value = "4" } }; // Assuming you have a list of hobbies to choose from ViewBag.Hobbies = new List<string> { "Reading", "Swimming", "Painting", "Cycling", "Hiking" }; return View(); } [HttpPost("Student/Create")] public IActionResult Create(Student student) { if (ModelState.IsValid) { return RedirectToAction("Successful"); } else { ViewBag.AllGenders = Enum.GetValues(typeof(Gender)).Cast<Gender>().ToList(); ViewBag.AllBranches = new List<SelectListItem>() { new SelectListItem { Text = "None", Value = "1" }, new SelectListItem { Text = "CSE", Value = "2" }, new SelectListItem { Text = "ETC", Value = "3" }, new SelectListItem { Text = "Mech", Value = "4" } }; //List of hobbies to choose from ViewBag.Hobbies = new List<string> { "Reading", "Swimming", "Painting", "Cycling", "Hiking" }; return View(student); } } public string Successful() { return "Student Created Successfully"; } } }
Let Us Understand the Create GET and POST Methods:
GET Method: Create
The [HttpGet(“Student/Create”)] attribute specifies that this action handles GET requests at the Student/Create URL path. This method prepares the view by populating ViewBag with data needed for the view:
- Gender Options: It uses Enum.GetValues(typeof(Gender)) to fetch all possible values from the Gender enum, converting them to a list and storing them in ViewBag.AllGenders. This is typically used to create Radion Buttons in the view.
- Branch Options: A list of SelectListItem objects represents different branches, such as CSE, ETC, and Mech. This list populates a dropdown for branch selection.
- Hobbies: A simple list of strings representing hobbies is stored in ViewBag.Hobbies can be used to generate checkboxes or a multi-select dropdown in the view. In our example, we are going to create checkboxes.
- The method then returns the Create view, which likely includes form elements that use these ViewBag data to allow the user to enter new student data.
POST Method: Create
The [HttpPost(“Student/Create”)] attribute indicates that this action handles POST requests at the same URL, which is typical for submitting form data. The method parameter, Student student, is automatically populated with data from the form submission through model binding.
- Model State Validation: ModelState.IsValid checks if the submitted data meets all validation rules set on the Student model. If it is valid, the method redirects to a success message action.
- Handling Invalid Data: If the data is not valid, the ViewBag is repopulated with the necessary data (similar to the GET method), and the view is re-rendered with the current student model data. This allows the user to correct any errors and resubmit the form.
Creating View:
Next, add Create.cshtml view and copy and paste the following code. The following View will display input elements, which we can use to create students.
@model Student @{ ViewBag.Title = "Create"; Layout = "~/Views/Shared/_Layout.cshtml"; } <div> <form asp-controller="Students" asp-action="Create" method="post" class="mt-3"> <div style="margin-top:7px" class="form-group row"> <label asp-for="Name" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input asp-for="Name" class="form-control" placeholder="Enter Your Name"> </div> </div> <div style="margin-top:7px" class="form-group row"> <label asp-for="Email" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input asp-for="Email" class="form-control" placeholder="Enter Your Email Id"> </div> </div> <div style="margin-top:7px" class="form-group row"> <label asp-for="Password" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input asp-for="Password" class="form-control" placeholder="Enter Your Password"> </div> </div> <div style="margin-top:7px" class="form-group row"> <label asp-for="Branch" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <select asp-for="Branch" class="custom-select mr-sm-2" asp-items="Html.GetEnumSelectList<Branch>()"></select> @*<select asp-for="Branch" asp-items="ViewBag.AllBranches"></select>*@ </div> </div> <div style="margin-top:7px" class="form-group row"> <label asp-for="IsRegular" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input type="checkbox" class="custom-control-input" asp-for="IsRegular"> </div> </div> <div style="margin-top:7px" class="form-group row"> <label asp-for="DateOfBorth" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <input asp-for="DateOfBorth" class="form-control"> </div> </div> <div style="margin-top:7px" class="form-group row"> <label asp-for="Gender" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> @foreach (var gender in ViewBag.AllGenders) { <label class="radio-inline"> <input type="radio" asp-for="Gender" value="@gender" id="Gender@(gender)" />@gender<br /> </label> } </div> </div> <div style="margin-top:7px" class="form-group row"> <label asp-for="Hobbies" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <style> .form-check { display: inline-block; margin-right: 20px; } </style> <div class="hobby-list" style="display: flex; flex-wrap: wrap;"> @{ int counter = 0; foreach (var hobby in ViewBag.Hobbies as List<string>) { <div class="form-check"> <label class="form-check-label" for="@("hobby" + counter)">@hobby</label> <input class="form-check-input" type="checkbox" name="Hobbies" value="@hobby" id="@("hobby" + ++counter)" /> </div> } } </div> </div> </div> <div style="margin-top:7px" class="form-group row"> <label asp-for="Address" class="col-sm-2 col-form-label"></label> <div class="col-sm-10"> <textarea asp-for="Address" class="form-control" placeholder="Enter Your Address"></textarea> </div> </div> <div style="margin-top:10px" class="form-group row"> <div class="col-sm-10"> <button type="submit" class="btn btn-primary">Create</button> </div> </div> </form> </div>
Testing the Application:
Now, run the application and navigate to the Student/Create URL, which should display the following View. Provide the necessary data and click on the Submit button as shown in the below image:
When the form is submitted, the values are mapped to the Student object parameter of the Post Create action method. You can inspect the student object while running the application in debugging mode, as shown in the image below. The Model binder in the ASP.NET Core application binds the posted form values to the Student object’s properties, which are passed as parameters to the Create() action method.
The value in the input element with the name attribute set to “Name” is mapped to the Name property of the Student object. Similarly, the value in the Branch input element will be mapped to the Branch property of the Student object. This will be the same for the other properties, like Email, Gender, etc.
In the next article, I will discuss Custom Model Binding in ASP.NET Core MVC with Examples. In this article, I explain Model Binding in an ASP.NET Core MVC Application with Complex Types with Examples. I hope you enjoy this article on Model Binding in ASP.NET Core MVC with Complex types.
the controller name in Create.cshtml given wrong name as students because of this during post page redirects to Students/create