简体   繁体   中英

Value cannot be null. Parameter name: items (in Dropdown List) ASP.NET MVC5

I have problem in my code. I'm using the registration form which comes with MVC5 , I added a field "Role" as a Dropdownlist to assign a role to the user while creating a new user. like in the below image: 在此处输入图片说明

Now in order to do that, I modified the "RegisterViewModel" and added the following properties:

        public IdentityRole Role { get; set; }

        [Required]
        [Display(Name = "Roles List")]
        public IEnumerable<IdentityRole> RolesList { get; set; }

In "AccountController", I changed the Register action, that gets the registration form, to become like this:

// GET: /Account/Register
        [AllowAnonymous]
        public ActionResult Register()
        {

            var _context = new ApplicationDbContext();
            var roles = _context.Roles.ToList();

            var viewModel = new RegisterViewModel
            {
                RolesList = roles
            };
            return View(viewModel);

        }

In the view "Register.cshtml" I added this Dropdownlist to load roles in the view and to post the role to the controller:

<div class="form-group">
        @Html.LabelFor(m => m.Role.Id, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.DropDownListFor(m => m.Role, new SelectList(Model.RolesList, "Name", "Name"), "Select Role", new { @class = "form-control" })
        </div>
    </div>

in the Register controller, in the the registration form post, I added this

// POST: /Account/Register
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser() { UserName = model.UserName , centerName = model.centerName };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    var role = new IdentityRole(model.Role.Name);

                    //I added this line to store the user and its roles in AspNetUserRoles table:
                    await UserManager.AddToRoleAsync(user.Id, role.Name);

                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    AddErrors(result);
                }
            }

Now when I try to register the user and post the form, I get following error:

Server Error in '/' Application.

Value cannot be null.
Parameter name: items

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: items

Source Error: 


Line 41:         @Html.LabelFor(m => m.Role.Name, new { @class = "col-md-2 control-label" })
Line 42:         <div class="col-md-10">
Line 43:             @Html.DropDownListFor(m => m.Role, new SelectList(Model.RolesList, "Name", "Name"), "Select Role", new { @class = "form-control" })
Line 44:         </div>
Line 45:     </div>

Source File: c:..\TransactionsSystem\Views\Account\Register.cshtml    Line: 43 

I tried different solutions to solve it, but nothing worked, can anybody please help or advise?

You cannot bind a <select> element to a complex object (which is what Role is) and when you submit the form, ModelState is invalid (your trying to bind the selected Name property of RolesList to typeof IdentityRole ). You then return the view, but have not repopulated the RolesList property, so its null (hence the error).

View models should not contain data models, and you view model should be

public class RegisterViewModel
{
    ....
    [Required(ErrorMessage = "Please select a role")]
    public string Role { get; set; }
    public IEnumerable<SelectListItem> RoleList { get; set; }
}

and in the GET method

var roles = _context.Roles.Select(r => r.Name);
var viewModel = new RegisterViewModel
{
    RolesList = new SelectList(roles)
};
return View(viewModel);

and in the view

@Html.LabelFor(m => m.Role, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
    @Html.DropDownListFor(m => m.Role, Model.RolesList, "Select Role", new { @class = "form-control" })
</div>

this will solve the invalid ModelState issue relating to Role , but if you do need to return the view because of other ModelState issues, then you must first repopulate the collection before returning the view

if (!ModelState.IsValid)
{
    var roles = _context.Roles.Select(r => r.Name);
    model.RolesList = new SelectList(roles);
    return View(model);
}
.... // save and redirect

Personally I would create the select list in your controller and pass it to the model, writing something like

  var RolesList = new List<SelectListItem>
            {
                new SelectListItem { Value = string.Empty, Text = "Please select a Role..." }
            };

   RolesList.AddRange(roles.Select(t => new SelectListItem
            {
                Text = t.role,
                Value = t.Id.ToString()
            }));` 

and then add this to your model, in your model you can then use

@Html.DropDownListFor(m => m.Role, Model.RoleList, new { @class = "form-control" })

This method/syntax works for me I am not sure if It is 'best practice' though

The solution that worked for me in MVC5 for editing an existing user

Model (part of)

public IEnumerable<string> Roles { get; set; }

View

@Html.DropDownListFor(model => model.UserRole, new SelectList(Model.Roles, Model.UserRole), new { @class = "form-control" })

Controller (Get)

 var model = new EditUserViewModel()
        {
            UserName = user.UserName,
            Email = user.Email,
            IsEnabled = user.IsEnabled,
            Id = user.Id,
            PhoneNumber = user.PhoneNumber,
            UserRole = userRoleName,

            // a list of all roles
            Roles = from r in RoleManager.Roles orderby r.Name select r.Name
        };

Make sure you add the list to the model before returning the view, or you will get this error. Example:

cshtml:

@model DelegatePortal.ViewModels.ImpersonateVendorViewModel


@using (Html.BeginForm("ImpersonateVendor", "Admin", FormMethod.Post))
{
    @Html.DropDownListFor(model => model.Id, new SelectList(Model.Vendors, "Id", "Name"), "Choose a vendor", new { @class = "form-control form-control-sm " })
}

controller:

// GET: /Admin/ImpersonateVendor
    public ActionResult ImpersonateVendor()
    {
        ImpersonateVendorViewModel model = new ImpersonateVendorViewModel();
        var vendors = (from c in db.Vendors
                        select c).ToList();
        model.Vendors = vendors; --> add list here
        return View(model);
    }

okay, i got the same problem, and none of the solution on this page helped me unless i figured it on my own.

while posting the form ,

when we use

If( ModelState.IsValid())
{
//post here


}

return view (model);

actually we are not initializing the items for dropdown list in the POST method. thats why this nullaugumentexception was thrown . we just need to initialize the list of items in the model inside the post method also as we did while calling the form, so that it can initialize the list if the model during the posting is not valid , and then put the model in return statement.

This answer is edited again, now it is working perfectly

I've come up with a new solution, I don't need to make the list required, I only can create property RoleName and make it required.

In RegisterViewModel , I only added these lines to the RegisterViewModel:

    [Display(Name = "Roles List")]
    public List<IdentityRole> RolesList { get; set; }

    [Required (ErrorMessage = "Please Select the Role")]
    public string RoleName { get; set; } 

In view I made the dropdown List like this

 <div class="form-group"> @Html.LabelFor(m => m.RoleName, new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.DropDownListFor(m => m.RoleName, new SelectList(Model.RolesList, "Name", "Name"), "Select Role", new { @class = "form-control" }) </div> </div> 

In AccountController.CS , The Register Action is like this:

[AllowAnonymous]
        public ActionResult Register()
        {
            var _context = new ApplicationDbContext();
            var roles = _context.Roles.ToList();

            var viewModel = new RegisterViewModel
            {
                RolesList = roles
            };
            return View(viewModel);           
        }

and the Register action that post the form, I made it like this:

public async Task<ActionResult> Register(RegisterViewModel model)
        {

            if (ModelState.IsValid)
            {
                var user = new ApplicationUser() { UserName = model.UserName , centerName = model.centerName };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    await UserManager.AddToRoleAsync(user.Id, model.RoleName);

                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    AddErrors(result);
                }
            }

            // If we got this far, something failed, redisplay form
            var _context = new ApplicationDbContext();
            var roles = _context.Roles.ToList();
            model.RolesList = roles;
            return View(model);
            }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM