MVC5: Delete Identity User results in Null Entry for Parameter ID? - asp.net-mvc

I have an administrator EDIT view created for USERS in my MVC5 application. Down in the bottom right I have a DELETE button that first asks the Admin logged in to confirm user deletion and then is supposed to execute the POST - Delete method in my UserManagement Controller. However when I confirm the deletion, I get an error of:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Threading.Tasks.Task`1[System.Web.Mvc.ActionResult] Delete(Int32)' in 'PROJECT.Areas.Admin.Controllers.UserManagementController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
This is my delete button on the View:
<div class="col-md-2" style="width:180px; float: right; padding-top: 12px; padding-bottom: 7px; border-bottom: 1px solid #dddddd; ">
#using (Html.BeginForm("Delete", "UserManagement", new { id = Model.Id }, FormMethod.Post))
{
#Html.AntiForgeryToken()
<input type="submit" class="btn btn-danger btn-xs" value="Delete User" onclick="return confirm('Are you sure you want to delete this User?');" />
}
</div>
And this is the [HttpPost] of my Delete action:
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Delete(int id)
{
ApplicationUser applicationUser = db.Users.Find(id);
if (applicationUser == null)
{
ModelState.AddModelError("", "Failed to find User ID for deletion.");
}
else
{
IdentityResult result = await UserManager.DeleteAsync(applicationUser);
if (result.Succeeded)
{
await db.SaveChangesAsync();
return RedirectToAction("Index", "UserManagement");
}
else
{
ModelState.AddModelError("", "Failed to Delete User.");
var errors = string.Join(",", result.Errors);
ModelState.AddModelError("", errors);
}
}
return View(applicationUser);
}
Clearly somewhere I am failing to get the ID passed from the view to the Action, but I'm not quite sure how to resolve the issue. I'm still faily new to MVC development, let alone ASP.Net Identity. Any assistance appreciated!
EDIT:
I've tweaked my approach a little, but am still getting the same error. I have a Delete Confirmation View which loads the Delete() GET method. The View resembles the following:
#model PROJECT.Models.ApplicationUser
#{
ViewBag.Title = "Delete";
Layout = "~/Areas/Admin/Views/Shared/_LayoutAdmin.cshtml";
string cancelEditUrl = "/Admin/UserManagement/";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this User?</h3>
#using (Html.BeginForm("Delete", "UserManagement", new { id = Model.Id }, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.ForumUsername)
#Html.HiddenFor(model => model.ReceiveSystemEmails)
#Html.HiddenFor(model => model.RegisteredDate)
#Html.HiddenFor(model => model.LastVisitDate)
#Html.HiddenFor(model => model.MemberOrgId)
#Html.HiddenFor(model => model.ProfilePictureSrc)
<div class="container">
<div class="row">
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Name, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="row">
<div class="editor-label">
#Html.LabelFor(model => model.Position)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Position, new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Position)
</div>
</div>
.....
<div class="row">
<div class="col-md-1" style="padding-left: 0px">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
<div class="col-md-9" style="padding-left: 0px">
Cancel
</div>
</div>
}
My Controller Actions for Delete():
//GET: Admin/UserManagement/Delete/5
public async Task<ActionResult> Delete(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ApplicationUser applicationUser = await UserManager.FindByIdAsync(id);
if (applicationUser == null)
{
return HttpNotFound();
}
return View(applicationUser);
}
// POST: Admin/UserManagement/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Delete(int id)
{
ApplicationUser applicationUser = db.Users.Find(id);
if (applicationUser == null)
{
ModelState.AddModelError("", "Failed to find User ID for deletion.");
}
else
{
IdentityResult result = await UserManager.DeleteAsync(applicationUser);
if (result.Succeeded)
{
await db.SaveChangesAsync();
return RedirectToAction("Index", "UserManagement");
}
else
{
ModelState.AddModelError("", "Failed to Delete User.");
var errors = string.Join(",", result.Errors);
ModelState.AddModelError("", errors);
}
}
return View(applicationUser);
}
ERROR:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Threading.Tasks.Task`1[System.Web.Mvc.ActionResult] Delete(Int32)' in 'PRISMdev.Areas.Admin.Controllers.UserManagementController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
I'm not quite sure why my Model ID is coming in as NULL still to the Delete() POST action.

Related

MVC Login modal not logging in

I'm using an html form to post form data for logging in. I put it in a modal but it does absolutely nothing other than redirect to main page, and append the verification token, id and password to the url. Is there any reason why? It doesn't even enter the account controller
I just took the post request from the default Login class that comes with mvc projects and put it inside the modal.
AccountController.cs:
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return PartialView("Login", model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Login.cshtml
#using Products.Models
#model LoginViewModel
#{
ViewBag.Title = "Log in";
}
<head>
<link rel="stylesheet" href="~/Content/loginmodal.css">
</head>
<!-- Modal -->
<div id="modal" class="modal">
<div class="modal-content">
<div class="modal-body">
<div class="Absolute-Center is-Responsive">
<div id="logo-container"></div>
<div class="col-sm-12 col-md-10 col-md-offset-1">
<form action="" id="loginForm">
#using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
#Html.TextBoxFor(m => m.Email, new { placeholder = "E-mail", #class = "form-control" })
#Html.ValidationMessageFor(m => m.Email, "", new { #class = "text-danger" })
</div>
<div class="form-group input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
#Html.PasswordFor(m => m.Password, new { placeholder = "Password", #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password, "", new { #class = "text-danger" })
</div>
<div class="form-group">
<input type="submit" value="Log in" class="btn btn-def btn-block" />
</div>
<div class="form-group text-center">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
Forgot Password | Support
</div>
}
</form>
</div>
</div>
</div>
</div>
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
LoginViewModel:
public class LoginViewModel
{
[Required]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
In your code you have <form action="" id="loginForm"> and Html.BeginForm() on the following line. The Html.BeginForm() renders another <form> element, nested in your case - which is not valid HTML. Browser probably takes the outer <form> where you specified action="", that is why it is not entering your Account controller.
remove this form
form action="" id="loginForm"
as you are creating 2 forms.
#using (Html.BeginForm . . .) creates a form dynamically which will hit your actionresult.

Passing data from view to method Create in controller [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 5 years ago.
I have the following problem with passing data to metod create in controller.
First I display view for method create (GET)
public ActionResult Create(string id)
{
RolesUser RolesUser = new RolesUser(id,repository.GetUserById(id).Roles, repository.GetRoles().ToList());
return View(RolesUser);
}
I use the following ViewModel
public class RolesUser
{
public RolesUser()
{
}
public RolesUser(string id,ICollection<IdentityUserRole> userRoles,List<IdentityRole> Roles)
{
userId = id;
this.Roles = GetNewRolesForUser(userRoles.ToList(), Roles.ToDictionary(x => x.Id, x => x.Name)).ConvertAll(
role =>
{
return new SelectListItem()
{
Text = role.ToString(),
Value = role.ToString(),
Selected = false
};
});
}
public IEnumerable<SelectListItem> Roles { get; set; }
public string userId { get; set; }
private List<string> GetNewRolesForUser(List<IdentityUserRole> UserRoles,Dictionary<string,string> Roles)
{
List<string> AvaiableRoles = new List<string>();
List<string> IdUserRoles = new List<string>();
UserRoles.ForEach(item => IdUserRoles.Add(item.RoleId));
foreach(KeyValuePair<string,string> Role in Roles)
{
if (!IdUserRoles.Contains(Role.Key))
{
AvaiableRoles.Add(Role.Value);
}
}
return AvaiableRoles;
}
}
It displays me essential information on my view in Create.cshtml, when I execute Submit it shows me following error
Object reference not set to an instance of an object.
The metod create (POST) looks like this
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles ="Admin")]
public ActionResult Create([Bind(Include ="userId")] RolesUser user)
{
if (ModelState.IsValid)
{
try
{
repository.AssignToRole(user.userId, "Klient");
repository.SaveChanges();
}
catch
{
ViewBag.exception = true;
return View();
}
}
ViewBag.exception = false;
return RedirectToAction("Index");
}
Code for Create.cshtml
#model Repository.ViewModels.RolesUser
#{
ViewBag.Title = "Create";
}
<h2>Dodaj poziom dostępu</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>#Model.userId</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.userId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DisplayFor(model => model.userId, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.Label("Poziomy dostępu", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.userId, Model.Roles)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Dodaj" class="btn btn-default" />
</div>
</div>
</div>
}
It looks like viewmodel is not passing to method, so I have null exception. But I don't understand why, if GET metod render properly view.
The NullReferenceException is likely hit inside the repository.AssignToRole() method because user.userId is null. You should add a hidden input for the userId somewhere inside the form so the userId value is posted to the parameter object on the Create action. If you add it after the #Html.AntiForgeryToken() the start of the form would look like
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.userId)
...

MVC controller.redirect doesn't redirect

I'm passing a string to Redirect but the controller is not sending the browser to the appropriate location.
The string is: "/Admin/SystemSecurity/_PermissionDetail/1"
The code is:
public ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
// Code get's here, but seems to go to /Submission/Index
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Submission");
}
In this case, the method that calls RedirectToLocal is _Login in the SubmissionController:
[ChildActionOnly]
public ActionResult _Login(string returnUrl)
{
if (Request.Cookies["UserName"] != null && !string.IsNullOrEmpty(Request.Cookies["UserName"].Value))
{
var loginModel = new Login { Email = Request.Cookies["UserName"].Value, ReturnUrl = returnUrl};
return PartialView(loginModel);
}
return PartialView();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> _Login(Login model, string returnUrl)
{
var isLoggedIn = UserLogin(model.Email, model.Password);
if (!isLoggedIn)
{
TempData["ErrorMessage"] = "Invalid email address or password.";
return RedirectToAction("Index", new { returnUrl = returnUrl });
}
// I make the call here, the values is correct here.
return RedirectToLocal(returnUrl);
}
Here's the Index method, also in SubmissionController:
public ActionResult Index(string message, string returnUrl)
{
IsAuthenticated();
if (!string.IsNullOrEmpty(message) )
AddMessage(message);
if (!string.IsNullOrEmpty((string)TempData["ErrorMessage"]))
{
AddError((string)TempData["ErrorMessage"]);
}
ViewBag.ReturnUrl = returnUrl;
return View();
}
After the POST _Login RedirectToLocal, the main Index method gets called again. Not sure who/what calls it. Probably something simple I'm missing.
For clarification I'm posting more of my View data here:
/Submission/Index:
#{
Layout = "~/Views/Shared/_Home.cshtml";
}
<div>
<p>...</p>
</div>
/Shared/_Home
#using PublicationSystem.ViewModels
#{
ViewBag.Title = "_Home";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container-fluid home-banner">
<!--Content from Index Page -------------------------------------->
<div class="clearfix"></div>
<div class="container">
<div class="row">
<div class="col-sm-4 col-md-3 col-lg-3">
<div class="left-side-blue">
...
</div>
</div>
<div class="col-sm-8 col-md-9 col-lg-9">
#{ Html.RenderPartial("_ErrorMessages"); }
#if (!ViewBag.IsAuthenticated)
{
Html.RenderAction("_Login", new { returnUrl = ViewBag.ReturnUrl });
}
else
{
<div class="hp-nav-boxes">...</div>
}
</div>
</div>
</div>
...
</div>
/Shared/_Login:
#model PublicationSystem.ViewModels.Login
<div class="login-box">
<div class="row">
#using (Html.BeginForm("_Login", "Submission", new { returnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "col-sm-6 col-md-4 col-lg-4 pull-right custm-login ipadsm4" }))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.TextBoxFor(m => m.Email, new { #class = "form-control", #placeholder = "Email" })
#Html.ValidationMessageFor(m => m.Email, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.PasswordFor(m => m.Password, new { #class = "form-control", #placeholder = "Password" })
#Html.ValidationMessageFor(m => m.Password, "", new {#class = "text-danger"})
</div>
<button type="submit" class="btn btn-default pull-right">
Login <span aria-hidden="true" class="glyphicon glyphicon-play"></span>
</button>
<div class="clearfix"></div>
}
</div>
The login logic works and the user can get logged in. It's just this redirect that's messing up.
You're calling _Login a partial, but partials don't have associated actions. It might be a child action, but you can't post to child actions. As result, _Login is just a standard action, and unless you post to it directly via something like:
#using (Html.BeginForm("_Login"))
{
...
}
And then submit that form on the resulting page, the action will never be hit.
Assuming you're actually doing it that way already, and just confused about the terminology, then the next place to look is at the inconsistency in return URL variables. Your action accepts returnUrl as a param, but you're only using that if the user is not logged in, where you redirect to Index with that return URL. However, if the user is logged in, then you're calling RedirectToLocal with model.ReturnUrl instead.

On MVC 5, after Signing In, Identity is still Unauthenticated

In my login page, I have sign-in code like this ...
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
However, when I check User.Identity right after Signin, it is still Unauthenticated
this.User.Identity.IsAuthenticated is returning false.
Anything I am missing ??
The this.User.Identity.IsAuthenticated is not true until the next request. In my case I redirect to the Home/Index. Once inside HomeController.Index() method I see that IsAuthenticated == true.
From here: http://msdn.microsoft.com/en-us/library/twk5762b.aspx
The forms-authentication ticket supplies forms-authentication information to the next request made by the browser.
I'm assuming you're using ASP.NET Identity. Here is a working template and see whats different in your code.
Model:
public class LoginViewModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
Controller:
//
// GET: /My/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// POST: /My/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid account name or password.");
}
}
return View(model);
}
Page:
#using (Html.BeginForm("Login", "My", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<hr />
#Html.ValidationSummary(false)
<div class="form-group">
<p class="col-md-2 control-label"><strong>Account</strong></p>
<div class="col-md-10">
#Html.TextBoxFor(m => m.UserName, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.UserName)
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Password, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Password, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Password)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Log in" class="btn btn-default" /> | #Html.ActionLink("Cancel", "Index", "Explore")
</div>
</div>
}

MVC 3 dropdown list not getting any values

I have a dropdownlist in an MVC 3 create page, however I am not getting any values when I POST the page. My code is as follows :-
View :-
#using (Html.BeginForm("Create", "League", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
League
<div class="editor-label">
#Html.LabelFor(model => model.League.fk_CountryID, "Country")
</div>
<div class="editor-field">
#Html.DropDownList("fk_CountryID", "--Select One--") *
#Html.ValidationMessageFor(model => model.League.fk_CountryID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.League.LeagueName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.League.LeagueName)
#Html.ValidationMessageFor(model => model.League.LeagueName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.League.Image)
</div>
<div class="editor-field">
Upload File: <input type="file" name="Image" />
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Controller :-
[HttpPost]
public ActionResult Create([Bind(Exclude = "Image")]Country country, HttpPostedFileBase Image, League league)
{
if (ModelState.IsValid)
{
model.League = league;
try
{
foreach (string file in Request.Files)
{
HttpPostedFileBase fileUploaded = Request.Files[file];
if (fileUploaded.ContentLength > 0)
{
byte[] imageSize = new byte[fileUploaded.ContentLength];
fileUploaded.InputStream.Read(imageSize, 0, (int)fileUploaded.ContentLength);
model.League.Image = imageSize;
}
}
db.Leagues.AddObject(model.League);
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception e)
{
ModelState.AddModelError("uploadError", e);
}
}
What am I doing wrong? Cannot get the dropdownlist value in the controller.
Thanks for your help and time
Ok fixed it by adding a SelectList in my ViewModel as follows :-
//Countries dropdown List
public SelectList CountryList { get; set; }
public string SelectedCountry { get; set; }
public LeagueData PopulateCountriesDDL(string selected, Country country)
{
var typeList = new SelectList(db.Countries.ToList(), "CountryID", "CountryName", selected);
LeagueData model = new LeagueData { CountryList = typeList, Country = country, SelectedCountry = selected };
return model;
}

Resources