asp.net-mvcformsmodelbinder

Invalid model state in Posting data with form in MVC5


I'm completely new in Asp.net MVC

I want to fill a form and post it to data base but my model Binder validation is false . and my Errors which I have in my model doesn't show

I'm sorry because I don't know what is the problem I couldn't shorten it:

here is my model:

public class Request
{
    //pkey
    public virtual int Id { get; set; }
    //Fkey
    public virtual int TourId { get; set; }

    [Required]
    [MaxLength(150, ErrorMessageResourceType = typeof(ErrorResource), ErrorMessageResourceName = "CheckLenght")]
    public virtual string FirstName { get; set; }


    [Required]
    [MaxLength(150, ErrorMessageResourceType = typeof(ErrorResource), ErrorMessageResourceName = "CheckLenght")]
    public virtual string LastName { get; set; }

    [Required]
    [EmailAddress(ErrorMessageResourceType = typeof(ErrorResource), ErrorMessageResourceName = "Email")]
    [MaxLength(150, ErrorMessageResourceType = typeof(ErrorResource), ErrorMessageResourceName = "CheckLenght")]
    public virtual string Email { get; set; }

    [Required]
    public virtual string Phone { get; set; }

    [MaxLength(100000000, ErrorMessageResourceType = typeof(ErrorResource), ErrorMessageResourceName = "CheckLenght")]
    public virtual string Comment { get; set; }


    public virtual bool FrequentTraveler { get; set; }

    [Required]
    [Range(1, 500000)]
    public virtual int TravelersCount { get; set; }


    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
    public virtual string Date { get; set; }


    public virtual bool ContactTimePreference { get; set; }

    [MaxLength(150, ErrorMessageResourceType = typeof(ErrorResource), ErrorMessageResourceName = "CheckLenght")]
    public virtual string Country { get; set; }


    public virtual bool Archived { get; set; }

and this is my form in view :

@using (Html.BeginForm("Create", "Request"))
{
  <div class="form-group">
   <input  type="hidden" name="TourId" value="4"/>
  </div>

 <div class="form-group">
 @Html.EditorFor(model => model.Request.FirstName, new { htmlAttributes = new { @class = "form-control" } })
 @Html.ValidationMessageFor(model => model.Request.FirstName, "", new { @class = "text-danger" , placeholder = "FirstName" })

 </div>



<div class="form-group">
 @Html.EditorFor(model => model.Request.LastName, new { htmlAttributes = new { @class = "form-control" } })
 @Html.ValidationMessageFor(model => model.Request.LastName, "", new { @class = "text-danger" })
</div>


<div class="form-group">                                       
@Html.EditorFor(model => model.Request.Email, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Request.Email, "", new { @class = "text-danger" })
</div>

<div class="form-group">
 @Html.EditorFor(model => model.Request.Phone, new { htmlAttributes = new { @class = "form-control" } })
 @Html.ValidationMessageFor(model => model.Request.Phone, "", new { @class = "text-danger" })
</div>


 <div class="form-group ft">
 @Html.EditorFor(model => model.Request.FrequentTraveler)
 @Html.ValidationMessageFor(model => model.Request.FrequentTraveler, "", new { @class = "text-danger" })
    </div>


<div class="form-group">
   <input type="hidden" name="TravelersCount" value="3" />
</div>

  <div class="form-group">
    <input type="hidden" name="TravelersCount" value="3" />
  </div>



}

I omit some of the form groups which they allow to be null for shorthand.

and this is my create action in Request controller:

 [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "Id,TourId,FirstName,LastName,Email,Phone,Comment,FrequentTraveler,TravelersCount,Date,ContactTimePreference,Country,Archived")] Request request)
    {
        if (ModelState.IsValid)
        {
            db.Requests.Add(request);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View();
    }

I appreciate if some one tell me what is the problem to make my request object valid.and if its possible what to do to send Errors to user when for example they put null in first name which its required with server.


Solution

  • ModelState will be invalid because your view is generating form controls for a model which is different from the model that you expect in the POST method. For example

    @Html.EditorFor(model => model.Request.FirstName, ...)
    

    is generating

    <input type="text" name="Request.FirstName" .... />
    

    but the model in the POST method is Request which does not have a property named Request - it only has one named FirstName so binding fails and ModelState is invalid because of the [Required] validation attribute.

    In order to bind to a Request model, the html would need to be

    <input type="text" name="FirstName" .... />
    

    In addition, your [Bind] attribute is also excluding the values from correctly binding since it does include "Request".

    Remove the [Bind] attribute (you seem to be wanting to bind everything anyway which is the default) and either change the model in the POST method to match the model you declared in the view, or use the Prefix property of BindAttribute

    public ActionResult Create([Bind(Prefix= "Request")] Request model)
    

    and finally delete the manually html for the hidden inputs and use @Html.HiddenFor(m => m.Request.TourId) etc so that the name attributes are consistent and will correctly bind.

    Finally, I recommend you read What is ViewModel in MVC?. Using data models in your views, particularly when editing forms is poor practice.

    Side note: You can use the following query in your POST methods to easily check the properties of your model that have validation errors

    var errors = ModelState.Keys.Where(k => ModelState[k].Errors.Count > 0).Select(k => new
    {
        propertyName = k,
        errorMessage = ModelState[k].Errors[0].ErrorMessage
    });