Reputation: 336
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?
Upvotes: 5
Views: 8121
Reputation: 1
This example is taken from Mosh Hamedani's MVC 5 course. He explained the Customer Id issue in the chapter 55. This can be resolved by passing a new customer() object in New method while creating a CustomerFormViewModel.
Upvotes: 0
Reputation: 55
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 )
Upvotes: 1
Reputation: 269
I am also going through this course. i have got a same issue. Add this line in the customerviewform
if (Model.Customers !=null)
{
@Html.HiddenFor(m => m.Customers.Id)
}
Why i have add because while hiddenfor which it is used for editing purpose if you remove also it will be problem. So add this line it will we be work and in customer model add public int? Membershiptype. And another thing while adding a new customer if you got error at dropdownlist then add this line before validation area return
customer.MembershipTypes = _Context.MembershipTypeTableset.ToList(); add this line before View("CustomerForm", viewModel)
Upvotes: 0
Reputation: 755
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);
}
Upvotes: 6
Reputation: 161
we are doing the same course and i ran into the exact same problem lol. i found a pretty nice workaround in my opinion.
just add these lines of code in the CustomerFormView
.
instead of
@Html.HiddenFor(m=>m.Customer.Id)
Add:
{
if (Model.Customer == null)
{
<input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Customer_Id" name="Customer.Id" type="hidden" value="0" />
}
else
{
@Html.HiddenFor(m => m.Customer.Id)
}
}
for some reason i saw that when i try to add a new customer , the value of id is an empty string instead of zero. therefore i changed it to zero manually in case the Customer object is null (which will always be the case when adding a new customer.) and it works fine for me.
let me know if you think this solution is problematic..
BTW Regarding your question : "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?"
Int data type is non nullable therefore it is implicitly required..same as the MembershipId (byte data type that doesnt have the [Required]
annotation.)
Upvotes: 0
Reputation: 110
hey change Id to CustomerId in model class. i think just 'Id' may be treated as Primary Key of that model class.
Upvotes: 0
Reputation: 336
After seeing this question, I made a workaround of my problem. I just disabled my Id
error in ModelState
at the very beginning of Save
action.
public ActionResult Save(Customer customer) {
ModelState["customer.Id"].Errors.Clear();
if ((!ModelState.IsValid) ) {
var viewModel = new CustomerFormViewModel {
Customer = customer,
MembershipTypes = _context.MembershipTypes.ToList()
};
return View("CustomerForm", viewModel);
}
if (customer.Id == 0) {
_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");
}
Now my Application works fine.
Upvotes: -1
Reputation: 571
This is just a rough draft as I don't have access to VS right now. Anyway, modify your Save action like so:
public ActionResult Save(CustomerFormViewModel customerVM) {
if (!ModelState.IsValid) {
return View(customerVM);
}
if (customer.Id == 0) {
_context.Customers.Add(customerVM.Customer);
}
else {
var CustomerInDb = _context.Customers.Find(customerVM.Customer.Id);
CustomerInDb.Name = customer.Name;
CustomerInDb.DateOfBirth = customer.DateOfBirth;
CustomerInDb.IsSubscribedToNewsLetter = customer.IsSubscribedToNewsLetter;
CustomerInDb.MembershipTypeId = customer.MembershipTypeId;
}
_context.SaveChanges();
return RedirectToAction("Index", "Customer");
}
Oh and you can remove the following from the view since this is for create page:
@Html.HiddenFor(m=>m.Customer.Id)
Upvotes: 0
Reputation: 2670
Perhaps the problem is that the Id
field is marked as an int
and not int?
. Putting a variable as int
the Model automatically assumes there's going to be a value for this property since it's not marked nullable
.
Try marking the Id
Property is int?
and see if the results are what you expect or not.
Upvotes: -1