I'm watching this ASP.NET MVC course. I have a customer
model
with these following attribute.
public class Customer {
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
[Display(Name = "Date of Birth")]
public DateTime? DateOfBirth { get; set; }
public bool IsSubscribedToNewsLetter { get; set; }
public MembershipType MembershipType { get; set; }
[Display(Name="Membership Type")]
public byte? MembershipTypeId { get; set; }
}
Note thate the Id has no Required
data annotation. But in my database, the Id is primary key and Identity is true for the column.
There is a ViewModel
consisting Customer
and MembershipType
models.
public class CustomerFormViewModel {
public IEnumerable<MembershipType> MembershipTypes { get; set; }
public Customer Customer { get; set; }
}
I have a View that creates new Customer
with Name
, DateOfBirth
, MembershipType
and IsSubscribedToNewsLetter
fields. It takes the CustomerFormViewModel
.
@using Vidly.Models
@model Vidly.ViewModel.CustomerFormViewModel
@{
ViewBag.Title = "New";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>@ViewBag.Message</h2>
@using (Html.BeginForm("Save", "Customer")) {
<div class="form-group">
@Html.LabelFor(m => m.Customer.Name,new{@class="control-label"})
@Html.TextBoxFor(m => m.Customer.Name, new { @class = "form-control" })
@Html.ValidationMessageFor(m=>m.Customer.Name)
</div>
<div class="form-group">
@Html.LabelFor(m => m.Customer.DateOfBirth, new { @class = "control-label"})
@Html.TextBoxFor(m => m.Customer.DateOfBirth,"{0:d MMM yyyy}", new { @class = "form-control" })
</div>
<div class="form-group">
@Html.LabelFor(m => m.Customer.MembershipTypeId, new { @class = "control-label"})
@Html.DropDownListFor(m => m.Customer.MembershipTypeId,new SelectList(Model.MembershipTypes,"Id","Name"), "Select Membership Type", new { @class = "form-control" })
</div>
<div class="checkbox">
<label>
@Html.CheckBoxFor(m => m.Customer.IsSubscribedToNewsLetter) Subscribed To Newsletter?
</label>
</div>
@Html.HiddenFor(m=>m.Customer.Id)
<button type="submit" class="btn btn-primary">Save</button>
}
Here is my Save
controller:
public ActionResult Save(Customer customer) {
if (!ModelState.IsValid) {
var viewModel = new CustomerFormViewModel {
Customer = customer,
MembershipTypes = _context.MembershipTypes.ToList()
};
return View("CustomerForm", viewModel);
}
if (customer.Id == 0 || customer.Id==null) {
_context.Customers.Add(customer);
}
else {
var CustomerInDb = _context.Customers.Single(c => c.Id == customer.Id);
CustomerInDb.Name = customer.Name;
CustomerInDb.DateOfBirth = customer.DateOfBirth;
CustomerInDb.IsSubscribedToNewsLetter = customer.IsSubscribedToNewsLetter;
CustomerInDb.MembershipTypeId = customer.MembershipTypeId;
}
_context.SaveChanges();
return RedirectToAction("Index", "Customer");
}
When I fill the CustomerForm
view and click the submit button, the ModelState.Isvalid()
method always comes false; resulting the first if statement
of the Save
method true. So I can't store any new customer.
I tried to debug
the application by putting breakpoint
on if (!ModelState.IsValid)
and saw that the Id
field is creating a error(saying "The Id field is required"). Why is it saying that Id
is required when it isn't? Does the ModelState.IsValid
method check the model at database level? I don't think so.
If I change the Customer
model's Id
property like this:public int? Id { get; set; }
and change the if statement by this,if ((!ModelState.IsValid) && (customer.Id==null) )
the application works fine.
Is there any other solution of this Id
problem?
I watched the same course and I am guessing the author updated since you watched it, as he demonstrated this exact type of issue. The issue is that when returning the View Model to the View on the New Action, the Customer property is not initialized so the Id is null hence the ModelState failure when trying to Save
Just change as below, so that when setting the viewModel, you initialize the Customer and the Id is then 0:
public ActionResult New()
{
var memberShipTypes = _context.MembershipTypes.ToList();
var viewModel = new CustomerViewModel
{
Customer = new Customer(),
MembershipTypes = memberShipTypes
};
return View("CustomerForm", viewModel);
}
Had a heck of a time with this, and created a workaround, but it seems that Mosh addresses this in a later section where he sets up the Movie Form.
https://codewithmosh.com/courses/222293/lectures/3684111
The short answer is to add @Html.Hidden("Movie.Id", (Model.Movie != null) ? Model.Movie.Id : 0)
to the MovieForm.cshtml.
He then describes a way to avoid hard-coding "Movie.Id" into the view (see https://github.com/mosh-hamedani/vidly-mvc-5/commit/e5b994581931a079ad87418ddcf9338e808bd821#diff-e94a8dc96403203b00e58238bb80101c )
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With