Author : MD TAREQ HASSAN | Updated : 2020/06/23

Model binding

Overview

Image courtesy: https://www.c-sharpcorner.com/article/introduction-to-asp-net-mvc-model-binding/

Model binding overview

Binding types:

Binding Source

By default, model binding gets data in the form of key-value pairs from the following sources in an HTTP request (sources are scanned in the order indicated in the list):

Binding targets

Model binding tries to find values for the following kinds of targets:

By default, a model state error isn’t created if no value is found for a model property. The property is set to null or a default value:

[BindRequired] attribute

Type conversion errors
If a source is found but can’t be converted into the target type, model state is flagged as invalid and:

Common binding annotations

Content negotiation

Disclaimer: the following explanation to make it easy to understand model binding (might not be accurate w.r.t.)

Data annotations

Model Binding example

Following example is for AspNetCore MVC (WebApp)

HoverInputModel.cs

public class HoverInputModel
{
  public int FirstName { get; set; }
  public int LastName { get; set; }
  public string FullName { get; set; }
  public string Email { get; set; }
  // will be null (because there will be no input field in the form for this prop)
  public string Bar { get; set; }
}

HoverController.cs

public class HoverController : Controller
{

    // ... ... ...

	// GET: Hover/Create
	public IActionResult Create()
	{
		return View();
	}
	

	// POST: Hover/Create
	[HttpPost]
	public async Task<IActionResult> Create([FromFrom]HoverInputModel hoverInputModel)
	{
		// check model state
		
		// process data
	}
	
	// ... ... ...
}

Create.cshtml

@model ModelBindingAndFormValidation.Models.HoverInputModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create Hover Info</h1>

<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="FirstName" class="control-label"></label>
                <input asp-for="FirstName" class="form-control" />
                <span asp-validation-for="FirstName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="LastName" class="control-label"></label>
                <input asp-for="LastName" class="form-control" />
                <span asp-validation-for="LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="FullName" class="control-label"></label>
                <input asp-for="FullName" class="form-control" />
                <span asp-validation-for="FullName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Email" class="control-label"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

When form is posted to server, form data will be bound to HoverInputModel.Props

Notes:

Model Validation

HoverInputModel.cs

public class HoverInputModel
{

	[Required]
	[Display(Name = "First Name")]
	[Range(5, 50)]
	public int FirstName { get; set; }


	[Required]
	[Display(Name = "Last Name")]
	[StringLength(50, MinimumLength = 5, ErrorMessage = "Last name must have at least 5 characters")]
	public int LastName { get; set; }


	[Required(ErrorMessage = "{0} is required")]
	[StringLength(100, MinimumLength = 10, ErrorMessage = "Name must have at least 10 characters")]
	[RegularExpression(@"^[a-zA-Z\s]+$", ErrorMessage = "Please, use letters in the name. Digits are not allowed.")]
	[Display(Name = "Full Name")]
	public string FullName { get; set; }


	[Required(ErrorMessage = "{0} is required")]
	[StringLength(100, MinimumLength = 1, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.")]
	public string Email { get; set; }


	// will be null
	public string Bar { get; set; }
}

HoverController.cs

public class HoverController : Controller
{

    // ... ... ...

	// GET: Hover/Create
	public IActionResult Create()
	{
		return View();
	}
	

	// POST: Hover/Create
	// To protect from overposting attacks, enable the specific properties you want to bind to, for 
	// more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
	[HttpPost]
	[ValidateAntiForgeryToken]
	public async Task<IActionResult> Create([FromFrom]HoverInputModel hoverInputModel)
	{
		if (ModelState.IsValid)
		{
			// perform EF core task here i.e. _context.Add(hoverInputModel);await _context.SaveChangesAsync();
			
			return RedirectToAction(nameof(Index));
		}
		
		return View(hoverInputModel);
	}
	
	// ... ... ...
}

Excluding model property

MVC

[Bind(nameof(FirstName), nameof(LastName), nameof(FullName))]
public class HoverInputModel
{
	// data annotation here
	public string FirstName { get; set; }


	// data annotation here
	public string LastName { get; set; }


	// data annotation here
	public string FullName { get; set; }


	// ignored (value will be null)
	public string Bar { get; set; }
}

// OR
public void SubmitData([Bind(include= ...)]HoverInputModel hoverModel)
{
   //... ... ...
}

API

public class MyModel
{
    [JsonIgnore]
    public string Name { get; set; }
	
    // ... ... ...
}

// Action:
[HttpPost]
public IActionResult Student([FromBody]MyModel model)

Prefixed form element

Annotation: [Bind(Prefix= "Foo")]DataModel model

public void SubmitData([Bind(Prefix= "Address")]AddressModel addressOnly)
{
   //even if id property was provided, model binder will ignore it
}

Custom validation attribute

See: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation#custom-attributes

Model Binding and Validation for Web API

Hover.cs

public class Hover
{
	// properties with data annotations
}

HoverController.cs

[ApiController]
public class HoverController : ControllerBase
{

    [HttpPost]
    public IActionResult AddFoo([FromBody]Foo foo) // JSON -> POCO
    {
		// following code is unnecessary in web api because Model validation errors automatically trigger an HTTP 400 response
		//if(!ModelState.Isvalid){
		//	return View(foo);
		//}
	
		// EF Core
		_fooDbContext.Add(foo);
       
		return Created(foo);
    }
}