I am creating an ASP.NET Core 6.0 web application.
My validation for my form is not working, and when I output the ModelState
errors, I see this:
Key: Email, Error: The Email field is required.
Key: Password, Error: The Password field is required
These errors do not show up under any of the forms on my page.
Whilst writing this post I didn't change any of the code but for some reason
Key: Password, Error: The Password field is required.
is no longer showing up now which confuses me even more.
My edit view has all the necessary fields and no matter what I try I'm not able to get rid of the errors.
To give an easier visualisation, this is how my page looks like:
Below is all the relevant code:
@model Replay.ViewModels.ApplicationUserRoleViewModel
@{
ViewData["Title"] = "Edit User";
}
<h1>Edit User</h1>
<form asp-controller="Account" asp-action="Edit" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ApplicationUser.Email" class="control-label"></label>
<input asp-for="ApplicationUser.Email" class="form-control" />
<span asp-validation-for="ApplicationUser.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ApplicationUser.FullName" class="control-label"></label>
<input asp-for="ApplicationUser.FullName" class="form-control" />
<span asp-validation-for="ApplicationUser.FullName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ApplicationUser.Password" class="control-label"></label>
<input asp-for="ApplicationUser.Password" class="form-control" />
<span asp-validation-for="ApplicationUser.Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ApplicationUser.Active" class="control-label"></label>
<input asp-for="ApplicationUser.Active" type="checkbox" class="form-check-input" />
<span asp-validation-for="ApplicationUser.Active" class="text-danger"></span>
</div>
<div class="form-group">
<table>
@for (int i = 0; i < Model.Roles.Count; i++)
{
<tr>
<td>
@Html.HiddenFor(m => m.Roles[i].Id)
@Html.HiddenFor(m => m.Roles[i].Name)
@Html.DisplayFor(m => m.Roles[i].Name)
</td>
<td>@Html.CheckBoxFor(m => m.Roles[i].Selected)</td>
</tr>
}
</table>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
C# code:
using Replay.Models;
namespace Replay.ViewModels
{
public class ApplicationUserRoleViewModel
{
public ApplicationUser ApplicationUser { get; set; }
public List<RoleViewModel> Roles { get; set; }
public ApplicationUserRoleViewModel()
{
}
public ApplicationUserRoleViewModel(ApplicationUser applicationUser, List<Role> roles, List<int> userRoleIds)
{
ApplicationUser = applicationUser;
Roles = roles.Select(r => new RoleViewModel
{
Id = r.Id,
Name = r.Name,
Selected = userRoleIds.Contains(r.Id)
}).ToList();
}
}
public class RoleViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Selected { get; set; }
}
}
using System.ComponentModel.DataAnnotations;
namespace Replay.Models;
public class ApplicationUser
{
[Key]
public int Id { get; set; }
public string Email { get; set; }
public string FullName { get; set; }
public string Password { get; set; }
public Boolean Active { get; set; }
}
using System.ComponentModel.DataAnnotations;
namespace Replay.Models
{
public class Role
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(ApplicationUserRoleViewModel viewModel)
{
if (!ModelState.IsValid)
{
foreach (var key in ModelState.Keys)
{
var state = ModelState[key];
foreach (var error in state.Errors)
{
System.Diagnostics.Debug.WriteLine($"Key: {key}, Error: {error.ErrorMessage}");
}
var roles = _dbContext.Role.ToList();
var userRoleIds = _dbContext.UserRoles
.Where(ur => ur.UserId == viewModel.ApplicationUser.Id)
.Select(ur => ur.RoleId).ToList();
viewModel.Roles = roles.Select(r => new RoleViewModel
{
Id = r.Id,
Name = r.Name,
Selected = userRoleIds.Contains(r.Id)
}).ToList();
return View(viewModel);
}
}
var user = _dbContext.ApplicationUser.Find(viewModel.ApplicationUser.Id);
if (user == null)
{
return NotFound();
}
user.Email = viewModel.ApplicationUser.Email;
user.FullName = viewModel.ApplicationUser.FullName;
user.Password = viewModel.ApplicationUser.Password;
user.Active = viewModel.ApplicationUser.Active;
var userRoles = _dbContext.UserRoles.Where(ur => ur.UserId == user.Id).ToList();
_dbContext.UserRoles.RemoveRange(userRoles);
var selectedRoleIds = viewModel.Roles
.Where(r => r.Selected)
.Select(r => r.Id).ToList();
foreach (var roleId in selectedRoleIds)
{
_dbContext.UserRoles.Add(new UserRole { UserId = user.Id, RoleId = roleId });
}
_dbContext.SaveChanges();
return RedirectToAction("Index");
}
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Replay.Models
{
public class UserRole
{
[Key]
public int Id { get; set; }
[ForeignKey("ApplicationUser")]
public int UserId { get; set; }
[ForeignKey("Role")]
public int RoleId { get; set; }
}
}
What have I tried:
ApplicationUser.Email
and ApplicationUser.Password
) and still got the errors. Maybe good interesting to know is that when removing the ApplicationUser.Email
and ApplicationUser.Password
from my view, it specifically said that it is now missing ApplicationUser.Email
, ApplicationUser.Password
, Email
and Password
.@Html.HiddenFor()
, which obviously wouldn't even compile since my view model doesn't even have an Email
nor Password
.?
to the Email
and Password
in my ApplicationUser
class.What was I expecting:
I was expecting the model to be valid, and not have some kind of ghost inputs, then invalidate it.
Thanks to @Dai and @Md-Farid-Uddin-Kiron I found the answer.
I had to change my ViewModel to not directly include an EF-Model.
My ApplicationUserRoleViewModel
changed to this and then it worked:
using System.ComponentModel.DataAnnotations;
using Replay.Models;
namespace Replay.ViewModels
{
public class ApplicationUserRoleViewModel
{
public int Id { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string FullName { get; set; }
[Required]
public string Password { get; set; }
public bool Active { get; set; }
public List<RoleViewModel> Roles { get; set; }
}
public class RoleViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Selected { get; set; }
}
}