Я создаю веб-приложение ASP.NET Core 6.0.
Моя проверка формы не работает, и когда я вывожу ошибки ModelState
, я вижу это:
Ключ: адрес электронной почты, ошибка: поле «адрес электронной почты» является обязательным.
Ключ: Пароль, Ошибка: Поле Пароль обязательно.
Эти ошибки не отображаются ни в одной из форм на моей странице.
Во время написания этого поста я не менял код, но по какой-то причине
Ключ: Пароль, Ошибка: Поле Пароль является обязательным.
больше не появляется, что меня еще больше смущает.
В моем представлении редактирования есть все необходимые поля, и что бы я ни пытался, я не могу избавиться от ошибок.
Чтобы упростить визуализацию, моя страница выглядит так:
Ниже приведен весь соответствующий код:
@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>
Код С#:
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; }
}
}
Что я пробовал:
ApplicationUser.Email
и ApplicationUser.Password
) и все равно получил ошибки. Может быть, интересно узнать, что при удалении ApplicationUser.Email
и ApplicationUser.Password
из моего поля зрения конкретно говорилось, что теперь отсутствуют ApplicationUser.Email
, ApplicationUser.Password
, Email
and Password
.@Html.HiddenFor()
, которое, очевидно, даже не скомпилируется, поскольку в моей модели представления нет ни Email
, ни Password
.?
к Email
и Password
в своем классе ApplicationUser
.Чего я ожидал:
Я ожидал, что модель будет валидной и не будет иметь каких-то призрачных входных данных, а затем аннулирует ее.
Да, я это имел в виду, спасибо за замечание, исправлю.
Проблема в том, что вы используете класс сущности EF в качестве ViewModel
. Не делай этого. Определите отдельный класс для представления опубликованных данных формы.
Я не совсем уверен, что изменить. Насколько я понимаю, мой ApplicationUserRoleViewModel
не является классом сущности EF? Не могли бы вы привести мне пример того, что должно отличаться/как вы видите, что мой ApplicationUserRoleViewModel
является классом сущности EF? Из определения я знаю, что это будет означать, что моя ViewModel напрямую сопоставляется с таблицей в моей базе данных, а это не так. Спасибо за помощь :)
Здравствуйте, удалось ли вам добиться какого-либо прогресса? У вас все еще та же проблема? @Dai сказал: используйте пользовательскую модель представления, которая определяет все свойства, которые вы используете в своем представлении, а затем, как только вы отправите форму, в вашем методе публикации вы можете получить значение из viewModel и передать их в свою модель домена. Означает, что не используйте модель домена непосредственно в вашей модели представления.
У меня все еще была проблема, я думаю, что понимаю, что вы двое имеете в виду, и реализовал ее. Теперь он правильно сохраняется! Большое спасибо! Я опубликую ответ с обновленным кодом через секунду.
Благодаря @Dai и @Md-Farid-Uddin-Kiron я нашел ответ.
Мне пришлось изменить свою ViewModel, чтобы она не включала напрямую EF-модель.
Мой ApplicationUserRoleViewModel
изменился на это, и тогда это сработало:
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; }
}
}
В качестве улучшения измените public List<RoleViewModel> Roles { get; set; }
на public List<RoleViewModel> Roles { get; } = new List<RoleViewModel>();
, чтобы свойство не было потенциально null
. Модуль связывания моделей ASP.NET отлично работает со свойствами списка, состоящими только из get
.
Когда в заголовке вы говорите «Поле ATTRIBUTE является обязательным», вы имеете в виду «Поле SomeFieldName является обязательным»? Атрибут работы что-то значит в .NET-стране.