Reputation: 89
I get this error when i try to post back my bound model for update:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult EditUser(Int32, EtlGui.ViewModels.UsersEdit)' in 'EtlGui.Controllers.UsersController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters
Here are my Actions:
public ActionResult EditUser(int id)
{
User user = Database.Session.Load<User>(id);
if (user == null)
return HttpNotFound();
return View(new UsersEdit
{
username = user.username,
email = user.email,
Roles = Database.Session.Query<Role>().AsEnumerable().Select(role => new RoleCheckbox
{
id = role.id,
ischecked = user.Roles.Contains(role),
name = role.name
}).ToList()
});
}
[HttpPost,ValidateAntiForgeryToken]
public ActionResult EditUser(int id,UsersEdit edit )
{
var user = Database.Session.Load<User>(id);
if (user == null)
return HttpNotFound();
syncRole(edit.Roles,user.Roles);
if (Database.Session.Query<User>().Any(u => u.username == edit.username && u.id != id))
ModelState.AddModelError("username","username must be unique");
if (!ModelState.IsValid)
return View(edit);
user.username = edit.username;
user.email = edit.email;
Database.Session.Update(user);
return RedirectToAction("UsersServices");
}
and my view:
@model EtlGui.ViewModels.UsersEdit
....
<h2 class="heading">Edit User @Model.username</h2>
<form action="@Url.Action("EditUser")" method="POST" class="login">
@Html.AntiForgeryToken()
@Html.ValidationSummary()
....
<div class="col-lg-3 label">user name :</div>
<div class="col-lg-4">
<input type="text" name="username" class="form-control" value="@Model.username">
</div>
<div class="col-lg-3 label">email :</div>
<div class="col-lg-4">
<input type="text" name="email" class="form-control" value="@Model.email">
</div>
<div class="col-lg-3 label">password :</div>
<div class="col-lg-4">
<input type="password" name="password" class="form-control">
</div>
<h1 class="heading">select Roles</h1>
@Html.Partial("_RoleEditor", Model.Roles)
<button type="submit" class="btn darkblue-btn">Update User</button>
</form>
Upvotes: 1
Views: 500
Reputation:
Your form does not include a value for an id
property so nothing for it is posted to the controller. You could include an input for it, but by default, the parameters in your GET method will be added to the forms action
attribute when you use the BeginForm
method. Delete your manual <form>
element and replace it with
@using (Html.BeginForm())
{
....
}
The html generated will include action="/yourController/EditUser/X"
where X is the value of id
in the GET method and will be bound to the id
parameter in the POST method.
There are multiple other issues with your code which will cause model binding to fail. Delete all your manual html and use the strongly typed HtmlHelper
methods to correctly generate your html for 2-way model binding and client side validation. The typical code for your username
property is
@Html.LabelFor(m => m.username)
// or @Html.LabelFor(m => m.username, "user name :") if you have not applied a [Display] attribute
@Html.TextBoxFor(m => m.username, new { @class="form-control" })
@Html.ValidationMessageFor(username)
and do not use @Html.Partial()
to generate form controls for a collection. Create an EditorTemplate
in /Views/Shared/EditorTemplates
named RoleCheckbox.cshtml
(to match the class name), with the following code
@model RoleCheckbox
@Html.HiddenFor(m => m.id)
@Html.HiddenFor(m => m.name)
@Html.LabelFor(m => m.ischecked, Model.name)
@Html.CheckBoxFor(m => m.ischecked)
@Html.ValidationMessageFor(m => m.ischecked)
and in the main view, replace @Html.Partial("_RoleEditor", Model.Roles)
with
@Html.EditorFor(m => m.Roles)
which will correctly generate the html for each item in the collection.
You can also consider using the [Remote]
attribute to check if the username
is unique with client side validation - refer How to: Implement Remote Validation in ASP.NET MVC
Upvotes: 1