Search code examples
asp.net-mvcasp.net-mvc-5

Model.IsValid failing


I have the following model:

public class Profile
{
    [Key]
    public int ProfileId { get; set; }
    public bool ProfileVisibility { get; set; }
    public string Description { get; set; }
    public bool? Gender { get; set; }
    public DateTime? Birthday { get; set; }

    public int? CityId { get; set; }
    public virtual City City { get; set; }

    public int? CountryId {get; set;}
    public virtual Country Country { get; set; }

    public string UserId { get; set; }
    public virtual ApplicationUser User { get; set; }

    public IEnumerable<SelectListItem> Countries { get; set; }
    public IEnumerable<SelectListItem> Cities { get; set; }
}

In order to Edit a Profile, I created the actions:

[Authorize(Roles = "Editor, Admin")]
public ActionResult Edit(string id)
{
    Profile pr = db.Profiles.Include("User").First(a => a.UserId == id);
    if (pr.UserId == User.Identity.GetUserId() || User.IsInRole("Admin"))
    {
        pr.Countries = GetAllCountries();
        pr.Cities = GetAllCities();
        return View(pr);
    }
    else
    {
        TempData["message"] = "Nu puteti modifica un profil care nu va apartine!";
        return RedirectToAction("Index");
    }
}

[HttpPut]
[Authorize(Roles = "Editor, Admin")]
public ActionResult Edit(string id, Profile requestProfile)
{
    requestProfile.Countries = GetAllCountries();
    requestProfile.Cities = GetAllCities();
    try
    {
        if (ModelState.IsValid)
        {
            Profile pr = db.Profiles.Find(id);
            if (TryUpdateModel(pr))
            {
                if (pr.UserId == User.Identity.GetUserId() || User.IsInRole("Admin"))
                {
                    pr = requestProfile;
                    pr.Country = db.Countries.Find(pr.CountryId);
                    pr.City = db.Cities.Find(pr.CityId);
                    pr.UserId = id;
                    pr.User = db.Users.Find(id);
                    pr.Birthday = DateTime.Now;
                    db.SaveChanges();
                    TempData["message"] = "Profilul a fost editat!";
                }
                else
                {
                    TempData["message"] = "Nu puteti modifica un profil care nu va apartine!";
                    return RedirectToAction("Index");
                }
            }
            return Redirect("/Profile/Show/");
        }
        else
        {
            return View(requestProfile);
        }
    }
    catch (Exception e)
    {
        return View(requestProfile);
    }
}

Where Country and City are 2 other models, and GetAllCountries and GetAllCities are 2 nonactions the return an IEnumerable<SelectListItem>.

The view I created for editing a Profile is this:

@model TangoApp.Models.Profile
@{
    ViewBag.Title = "Edit";
}

<h2>Edit profile</h2>

<h2>Edit profile</h2>

@using (Html.BeginForm(actionName: "Edit", controllerName: "Profile"))
{
    @Html.HttpMethodOverride(HttpVerbs.Put)

    @Html.HiddenFor(m => m.ProfileId)

    <p>Profile vizibility:</p>
    <p>@Html.RadioButtonFor(Model => Model.ProfileVisibility, "true") public </p>
    <p>@Html.RadioButtonFor(Model => Model.ProfileVisibility, "false") private </p>

    @Html.HiddenFor(m => m.UserId)
    @Html.HiddenFor(m => m.User)
    @Html.HiddenFor(m => m.Cities)
    @Html.HiddenFor(m => m.Countries)
    @Html.HiddenFor(m => m.Birthday)
    @Html.Label("Text", "DDescription content")
    <br />
    @Html.EditorFor(m => m.Description)
    <br />
    @Html.ValidationMessageFor(m => m.Description)
    <br />

    <p>Gender:</p>
    <p>@Html.RadioButtonFor(Model => Model.Gender, "true") female </p>
    <p>@Html.RadioButtonFor(Model => Model.Gender, "false") male </p>

    <label>Selectati Tara</label>
    @Html.DropDownListFor(m => m.CountryId,
        new SelectList(Model.Countries, "Value", "Text"),
        "Select country", new { @class = "form-control" })
    @Html.ValidationMessageFor(m => m.CountryId, "", new { @class = "text-danger" })
    <br />

    <label>Selectati Orasul</label>
    @Html.DropDownListFor(m => m.CityId,
        new SelectList(Model.Cities, "Value", "Text"),
        "Select city", new { @class = "form-control" })
    @Html.ValidationMessageFor(m => m.CountryId, "", new { @class = "text-danger" })
    <br />

    <button class="btn btn-sm btn-succes" type="submit">Edit profile</button>
}

I figured that the problem is that it fails the ModelState.IsValid but I can't figure out why. Can someone help me?


Solution

  • If you attach a debugger, then you can look at ModelState.Keys and ModelState.Values, and esp. ModelState.Values[i].Errors. These entries will show which Model item(s) are flagged as invalid by the Model Binder and why.

    It could be as simple has having an int or bool Model item which is not supplied, because all value types (including int and bool) are by definition required, whether you specify so using attributes or not.