ModelState validation does not work with DropDownList and nested objects

In our application, we have a Domain layer that contains classes with DataAnnotations for validation.

We use these classes in our models at our ASP.NET MVC ui level.

For instance:

Domain Level:

public class Company { public int Id { get; set; } [Required] [StringLength(50)] public string Description { get; set; } // ... some model logic abreviated } public class Supplier { public int Id { get; set; } [Required] public string Name { get; set; } [Required] public Company Company { get; set; } // ... some model logic abreviated } 

In our ASP.NET MVC presentation layer:

 public class SupplierEditModel { public Supplier Supplier { get; set; } public IEnumerable<Company> Company { get; set; } public int SelectedCompany { get; set; } // ... some model logic abreviated } 

In this case, we have a page with DropDownList companies. The list has a binding:

 @Html.DropDownListFor(m => m.SelectedCompany, new SelectList(Model.Companies, "Id", "Description", Model.SelectedCompany)) 

Our problem is the POST method of our controller, when we check ModelState.IsValid, the model is invalid because the provider. The company is NULL. Then we can get the company using SelectedCompany, but our problem is that it means that we cannot do something like this:

 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(SupplierEditModel model) { if (ModelState.IsValid) { model.CreateSupplier(_supplierService); return RedirectToAction("Index"); } return RedirectToAction("Create"); } 

We would like to use verification before creating a supplier.

+4
source share
3 answers

I see that you have (at least) two options:

You can flatten your review model and omit Supplier.Company if it is not needed in your view.

 public class SupplierEditModel { public int SupplierId { get; set; } public string Name { get; set; } public Company Company { get; set; } public IEnumerable<Company> Company { get; set; } public int SelectedCompany { get; set; } // ... some model logic abreviated } 

(Note: your data annotations should be in the view model, not in the domain model.)

or

You can clear ModelState error before checking IsValid property

 ModelState.Remove(string key, ModelState); 

The best practice here is likely to be to use a flattened model for two reasons. First, as a rule, it’s good practice to send only what he needs and nothing more. If the submission does nothing with the Provider. Company, this should not be part of the model. Two, using the ModelState.Remove method, although effective, may be considered by some as small kludgy. You can get your chops a bit in the code review;)

+4
source

You can remove Company from ModelState before checking IsValid : ModelState.Remove("Supplier.Company")

+2
source
  public class OrderViewModel { [Display(Name = "Customer")] [Required(ErrorMessage = "Please select customer.", AllowEmptyStrings = false)] public string CustomerName { get; set; } public IEnumerable<CustomerViewModel> Customers { get; set; } } public class CustomerViewModel { public string CustomerID { get; set; } public string CompanyName { get; set; } } <div class="form-group"> @Html.LabelFor(m => m.CustomerName, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.DropDownListFor(m => m.CustomerName, new SelectList(Model.Customers, "CustomerId", "CompanyName"), "- Please Select -", new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.CustomerName, null, new { @class = "text-danger" }) </div> </div> 
-1
source