Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.net MVC ModelState.Isvalid returning false for Id

Tags:

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?

like image 384
Ahashan Alam Sojib Avatar asked Sep 08 '17 04:09

Ahashan Alam Sojib


2 Answers

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);
    }
like image 99
Darthchai Avatar answered Oct 11 '22 12:10

Darthchai


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 )

like image 30
Don Pickard Avatar answered Oct 11 '22 12:10

Don Pickard