Ichigo Kurosaki
Ichigo Kurosaki

Reputation: 215

System.ArgumentNullException: 'Value cannot be null. Parameter name: items'

This is my index.cshtml:

<div class="form-group">
@Html.LabelFor(model => model.MembershipID, "MembershipID", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
    @Html.DropDownList("items", new SelectList((IEnumerable<SelectListItem>)ViewData["MembershipID"]), new { @class = "form-control" })
    @Html.ValidationMessageFor(model => model.MembershipID, "", new { @class = "text-danger" })
</div>

This is my StudentController.cs:

public ActionResult Create()
{
    List<SelectListItem> items = new List<SelectListItem>();

    items.Add(new SelectListItem { Text = "Child", Value = "0" });

    items.Add(new SelectListItem { Text = "Teen", Value = "1" });

    items.Add(new SelectListItem { Text = "Adult", Value = "2" });

    items.Add(new SelectListItem { Text = "Other", Value = "3" });

    items.Add(new SelectListItem { Text = "check", Value = "4" });


    ViewData["MembershipID"] = items;

    return View();
}

Controller post method:

[HttpPost] 
public ActionResult Create(Student student) 
{ 
    try 
    { 
        // TODO: Add insert logic here 
        using (BlackBeardDBEntities db = new BlackBeardDBEntities()) 
        { 
            db.Students.Add(student); 
            db.SaveChanges(); 
        } 
        return RedirectToAction("Index"); 
    } 
    catch 
    { 
        return View(); 
    }
}

Upvotes: 1

Views: 2201

Answers (2)

haldo
haldo

Reputation: 16701

Update

From your comment below I can see you have this piece of code:

[HttpPost] 
public ActionResult Create(Student student) 
{ 
    ...
    return RedirectToAction("Index"); 
    ...
}

You are performing a redirect here. ViewData only lives for a single request, so what's happening is when the browser redirects to the Index action the ViewData is empty (because it was sent in the request which told the browser to redirect).

This is why you should not use ViewData for these types of task, use a ViewModel instead.

However, to solve your problem don't redirect. Just return the Index view straight from the Create POST method (remember the data must be passed to the view with each request, so you will need to populate ViewData["MembershipID"] again):

[HttpPost] 
public ActionResult Create(Student student) 
{ 
    ...
    // populate ViewData here, before returning the view
    ViewData["MembershipID"] = GetItems();
    return View("Index"); 
    ...
}

Create a method (GetItems in my example, I'll leave the implementation details to you) to return the items since you use them in multiple places.

ViewData will be preserved in this request since it's returning straight to Index rather than performing a redirect.

Upvotes: 1

Vinod Vutpala
Vinod Vutpala

Reputation: 843

From the above conversation, you could have your controller as follows

public class StudentsController : Controller
{

    private void _SetViewData()
    {
        List<SelectListItem> items = new List<SelectListItem>();
        items.Add(new SelectListItem { Text = "Child", Value = "0" });
        items.Add(new SelectListItem { Text = "Teen", Value = "1" });
        items.Add(new SelectListItem { Text = "Adult", Value = "2" });
        items.Add(new SelectListItem { Text = "Other", Value = "3" });
        items.Add(new SelectListItem { Text = "check", Value = "4" });
        ViewData["MembershipID"] = items;
    }

    // GET: Students
    public ActionResult Index()
    {
        // In case your index view uses viewdata, call _SetViewData() if  not remove.
        _SetViewData();
        return View();
    }

    // GET: Students/Create
    public ActionResult Create()
    {
        _SetViewData();
        return View();
    }

    [HttpPost]
    public ActionResult Create(Student student)
    {
        try
        { 
            // TODO: Add insert logic here 
            using (BlackBeardDBEntities db = new BlackBeardDBEntities()) 
            { 
                db.Students.Add(student); db.SaveChanges(); 
            }                 
            return RedirectToAction("Index"); 
        } 
        catch {
            // Either you can redirect to the action which will reload the viewdata
            // return RedirectToAction("Create");
            // OR you could load the viewdata and use the same view
            _SetViewData();
            return View();
        } 
    } 
}

Here, you may use a private method to set your viewdata needed for an action. If you are returning the view, call this method to set viewdata if not, redirect to Create action which will call the get action and sets the viewdata.

Hope this helps you.

Upvotes: 0

Related Questions