0

I apologize if this is a dumb question, but I am stumped. This is my first MVC application I am attempting, coming from web forms. I have the following models:

public class IndividualItem
{
    [Required][Key]
    public string SerialNumber { get; set; }
    public virtual NSN NSNnumber { get; set; }
    public string Location { get; set; }
    public string User { get; set; }
}

and

public class NSN
{
    [Required][DisplayName("NSN")][StringLength(13)][Key]
    public string NSNnumber { get; set; }
    [Required]
    [StringLength(100)]
    [DisplayName("NSN Description")]
    public string Description { get; set; }
    public virtual ICollection<IndividualItem> Items { get; set; }
}

And here is the View Model

public class IndividualItemNSNselectList
{
    public SelectList NSNlist { get; set; }
    public IndividualItem IndividualItem { get; set; }
}

Controller

public ActionResult Create()
    {
        var view = new IndividualItemNSNselectList();
        view.NSNlist = new SelectList(db.NSNs.ToList(),"NSNnumber","Description");
        return View(view);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "SerialNumber,Location,User")] IndividualItem individualItem)
    {
        if (ModelState.IsValid)
        {
            db.IndividualItem.Add(individualItem);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(individualItem);
    }

and drop down in view

<div class="form-group">
            @Html.LabelFor(model => model.IndividualItem.NSNnumber, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(model => model.IndividualItem.NSNnumber,Model.NSNlist, " ")
                @Html.ValidationMessageFor(model => model.IndividualItem.NSNnumber, "", new { @class = "text-danger" })
            </div>
        </div>

When I try to make the create view for the IndividualItem everything works, including the population of the drop down list. However, when I post the NSN object is not being input to the database.

I have done a lot of researching that lead me to this point but I cannot figure out what is wrong. Thank you for you assistance, let me know if you need me to clarify anything.

adiga
  • 34,372
  • 9
  • 61
  • 83
Nick McGinnis
  • 143
  • 13

2 Answers2

3

You have multiple errors in your code, and some bad practices.

First you cannot bind a <select> to a property which is a complex object (it only posts back a simple value - the value of the selected option). Assuming your want to bind to the NSNnumber of NSN, then the view code needs to be

@Html.DropDownListFor(m => m.IndividualItem.NSNnumber.NSNnumber, Model.NSNlist, "")

as Saket has noted.

Second the model in your view is IndividualItemNSNselectList which means the model in your POST method parameter must also be IndividualItemNSNselectList. your POST method signature should be

public ActionResult Create IndividualItemNSNselectList model)

and the only reason your getting anything in the POST method is because you named the parameter the same as the property which can cause other issues, including the fact that when you return the view because ModelState is invalid your will get an exception (refer The model item passed into the dictionary is of type .. but this dictionary requires a model item of type for more detail) because you pass back an instance of IndividualItem to a view expecting IndividualItemNSNselectList.

All this can easily be solved by using view model correctly. View models should not contain properties which are data models, only properties of your data models that you want to display/edit in the view. In addition you never needs a [Bind] attribute when using a view model since you already protected against over posting attacks.Refer What is ViewModel in MVC?.

Your [Bind] attribute suggests you have only 4 properties your editing,so your view model should be (add display and validation attributes as required)

public class IndividualItemVM
{
    public string SerialNumber { get; set; }
    public string NSNnumber { get; set; }
    public string Location { get; set; }
    public string User { get; set; }
    public IEnumerable<SelectListItem> NSNlist { get; set; }
}

and the view becomes

@Html.DropDownListFor(m => m.NSNnumber, Model.NSNlist, "", new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.NSNnumber, "", new { @class = "text-danger" })

The POST method signature now becomes

public ActionResult Create IndividualItemVM model)

and in the method, you create a new instance of the data model(s) and map their properties from the view model and save.

In addition, you must also repopulate the SelectList before you return the view or another exception will occur with your current code as described in The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable'. As a side note, using .ToList() in your query for generating the SelectList is unnecessary.

  • Thank you so much for your response. I greatly appreciate your response as it cleared up how to use the ViewModel correctly. – Nick McGinnis Jun 21 '17 at 14:02
0

There are two modifications required in your code:

Change 1: Dropdown list should be bound to a property and not a class

Update your dropdown to:

@Html.DropDownListFor(model => model.IndividualItem.NSNnumber.NSNnumber, Model.NSNlist, " ")

Change 2:

Bind attribute in action method is missing the required attribute to be bound. Update your post action method as below:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "SerialNumber,Location,User,NSNnumber")] IndividualItem individualItem)
{
}

Or if your intention is to exclude the attributes, modify as below:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Exclude= "SerialNumber,Location,User")] IndividualItem individualItem)
{
}

Hope this works for you. Verified it myself.

Saket Kumar
  • 4,363
  • 4
  • 32
  • 55
  • I implemented the changes you suggested and I am getting the following error on post now: The model item passed into the dictionary is of type 'HandReceipt.Models.IndividualItem', but this dictionary requires a model item of type 'HandReceipt.ViewModels.IndividualItemNSNselectList'. – Nick McGinnis Jun 20 '17 at 19:44
  • This is entirely a different issue. You might want to look here for this: https://stackoverflow.com/questions/2315129/the-model-item-passed-into-the-dictionary-is-of-type-mvc-models-modela-but-thi – Saket Kumar Jun 20 '17 at 19:51