houman_ag
houman_ag

Reputation: 237

Issue with Model Binding

I have created a View Model called CompetitionRoundModel which is partially produced below:

public class CompetitionRoundModel
{
    public IEnumerable<SelectListItem> CategoryValues
    {
        get
        {
            return Enumerable
                .Range(0, Categories.Count())
                .Select(x => new SelectListItem
                {
                    Value = Categories.ElementAt(x).Id.ToString(),
                    Text = Categories.ElementAt(x).Name
                });
        }
    }

    [Display(Name = "Category")]
    public int CategoryId { get; set; }

    public IEnumerable<Category> Categories { get; set; }

    // Other parameters
}

I have structured the model this way because I need to populate a dropdown based on the value stored in CategoryValues. So for my view I have:

@using (Html.BeginForm())
{
    <div class="form-group">
        @Html.LabelFor(model => model.CategoryId, htmlAttributes: new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            @Html.DropDownListFor(model => model.CategoryId, Model.CategoryValues, new { @class = "form-control" })
            @Html.ValidationMessageFor(model => model.CategoryId, "", new { @class = "text-danger" })
        </div>
    </div>
    // Other code goes here
}

I have selected model.CategoryId in the DropDownListFor() method since I want to bind the selected value to CategoryId. I really don't care for CategoryValues, I just need it to populate the DropDown.

My problem now is that when my Controller receives the values for my Model in the action method, CategoryValues is null which causes the system to throw a ArgumentNullException (the line that is highlighted is the return Enumerable line.

I have even tried [Bind(Exclude="CategoryValues")] but no change at all. Any help would be much appreciated.

Upvotes: 2

Views: 78

Answers (2)

user3559349
user3559349

Reputation:

Your not (and should not be) creating form controls for each property of each Category in your IEnumerable<Category> collection so in your POST method, the value of Categories is null (it never gets initialized). As soon as you attempt CategoryValues and exception is thrown by your .Range(0, Categories.Count()) line of code in the getter.

Change you view model to give CategoryValues a simple geter/setter, and delete the Categories property

public class CompetitionRoundModel
{
    public IEnumerable<SelectListItem> CategoryValues { get; set; }
    [Display(Name = "Category")]
    public int CategoryId { get; set; }
    .... // Other properties
}

and populate the SelectList in the controller methods, for example

var categories db.Categories; // your database call
CompetitionRoundModel model = new CompetitionRoundModel()
{
    CategoryValues = categories.Select(x => new SelectListItem()
    {
        Value = x.Id.ToString(),
        Text = x.Name
    },
    ....
};
return View(model);

or alternatively

CompetitionRoundModel model = new CompetitionRoundModel()
{
    CategoryValues = new SelectList(categories, "Id", "Name" ),

Note also that if you return the view (because ModelState is invalid, the you need to repopulate the value of CategoryValues (refer The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable' for more detail)

Upvotes: 1

Brian Mains
Brian Mains

Reputation: 50728

Since CategoryValues just populates the drop down, it will never post back to the server and you'll need to rebuild the list from the database before using it in the GET or POST operation. The CategoryId property is the value that will be posted back to the server from the DropDownList.

Upvotes: 0

Related Questions