如何让 ModelState 验证通过带有外键的视图模型提交的数据?
How to get ModelState to Validate Data Submitted Through a View Model With Foreign Keys?
注意:请忽略密码未安全存储的事实;本项目仅供自己学习,以后会解决这个问题
我有三个模型,Account、AccountType 和 Person:
public class Account
{
[Key]
public int AccountID { get; set; }
[ForeignKey("AccountType")]
public int AccountTypeID { get; set; }
[ForeignKey("Person")]
public int PersonID { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
public DateOnly DateCreated { get; set; } = DateOnly.FromDateTime(DateTime.Now);
[Required]
public DateOnly DateModified { get; set; } = DateOnly.FromDateTime(DateTime.Now);
public virtual AccountType AccountType { get; set; }
public virtual Person Person { get; set; }
}
public class AccountType
{
[Key]
public int AccountTypeID { get; set; }
[Required]
public string AccountTypeName { get; set; }
}
public class Person
{
[Key]
public int PersonID { get; set; }
[Required]
[DisplayName("First Name")]
public string FirstName { get; set; }
[Required(AllowEmptyStrings = true)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[DisplayName("Middle Name")]
public string MiddleName { get; set; }
[Required]
[DisplayName("Last Name")]
public string LastName { get; set; }
[Required]
[DisplayName("Email")]
public string Email { get; set; }
[Required(AllowEmptyStrings = true)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[DisplayName("Phone")]
public string Phone { get; set; }
[Required]
public DateOnly CreatedDate { get; set; } = DateOnly.FromDateTime(DateTime.Now);
[Required]
public DateOnly ModifiedDate { get; set; } = DateOnly.FromDateTime(DateTime.Now);
}
因为我想在一个视图中使用所有这三个模型,所以我为它们创建了一个 Register 视图模型:
public class Register
{
public List<SelectListItem> AccountTypes { get; set; }
public Account Account { get; set; }
public Person Person { get; set; }
}
这是服务于视图的相关控制器的 GET 操作方法:
public IActionResult Register()
{
var Register = new Register();
Register.AccountTypes = _db.AccountTypes.Select(accType => new SelectListItem
{
Value = accType.AccountTypeID.ToString(),
Text = accType.AccountTypeName
}).ToList();
return View(Register);
}
这里是注册视图本身:
@model Register
<form method="post">
<div class="row mb-2">
<div class="col-12">
<select asp-for="Account.AccountTypeID"
asp-items="Model.AccountTypes"
class="form-select"
aria-label="Default select example"
></select>
</div>
</div>
<div class="row mb-2">
<div class="col-12">
<label asp-for="Account.Username" class="d-block"></label>
<input asp-for="Account.Username" class="w-100" />
<span asp-validation-for="Account.Username" class="text-danger"></span>
</div>
<div class="col-12">
<label asp-for="Account.Password" class="d-block"></label>
<input asp-for="Account.Password" class="w-100" />
<span asp-validation-for="Account.Password" class="text-danger"></span>
</div>
</div>
<partial name="/Views/Person/_Create.cshtml" for="Person" />
<button type="submit" class="btn btn-primary">Create</button>
</form>
我现在正处于编写控制器 POST 操作方法的阶段,目的是将视图中的绑定字段提交到数据库中,我正在 运行 进行一些麻烦:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Register(Register Register)
{
if (ModelState.IsValid)
{
_db.Persons.Add(Register.Person);
_db.Accounts.Add(Register.Account);
_db.SaveChanges();
}
return View(Register);
}
当我在 ModelState.IsValid
行放置一个标记时,我看到它是 false
。我了解这是因为 3 个模型字段具有空值/无效值:
AccountTypes
Account.AccountType
Account.Person
我需要在 Register 模型中 AccountTypes
以在视图的下拉列表/select 字段中显示帐户类型列表。然后,Account.AccountType
和 Account.Person
也是必需的,因为它们建立了实体表之间的关系。所以,我想我无法从模型中删除这些字段。
我考虑过以某种方式将这些特定字段从验证过程中排除,同时将它们保留在模型中,但我不确定如何去做。此外,我有一种挥之不去的感觉,可能有更好的方法来处理我不知道的整个过程。
那么,让 ModelState 接受 POST 注册请求的最正确方法是什么?
在您的个人模型中,有几个字段标记为必填属性。由于此 ModelState 始终为 false,因为这些字段需要值并且视图或控制器(GET 调用)不提供 them.If 可能您可以删除需要的字段或为没有必填字段的人创建单独的模型。
然后要获得 selected AccountType,您需要像这样更改您的 Register 模型。
public class Register
{
public Register()
{
Person = new Person();
Account = new Account();
}
public int AccountTypeId { get; set; }
public List<SelectListItem> AccountTypes { get; set; }
public Account Account { get; set; }
public Person Person { get; set; }
}
并像这样在视图中更改 select。
<select asp-for="AccountTypeId"
asp-items="Model.AccountTypes"
class="form-select"
aria-label="Default select example"
></select>
显然您使用的是 net6。你有两个选择,让这个属性可以为空(也许更多)
public virtual AccountType? AccountType { get; set; }
public virtual Person? Person { get; set; }
或者为了防止以后出现这种情况 类 您可以从项目配置中删除 Nullable
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<!--<Nullable>enable</Nullable>-->
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
注意:请忽略密码未安全存储的事实;本项目仅供自己学习,以后会解决这个问题
我有三个模型,Account、AccountType 和 Person:
public class Account
{
[Key]
public int AccountID { get; set; }
[ForeignKey("AccountType")]
public int AccountTypeID { get; set; }
[ForeignKey("Person")]
public int PersonID { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
public DateOnly DateCreated { get; set; } = DateOnly.FromDateTime(DateTime.Now);
[Required]
public DateOnly DateModified { get; set; } = DateOnly.FromDateTime(DateTime.Now);
public virtual AccountType AccountType { get; set; }
public virtual Person Person { get; set; }
}
public class AccountType
{
[Key]
public int AccountTypeID { get; set; }
[Required]
public string AccountTypeName { get; set; }
}
public class Person
{
[Key]
public int PersonID { get; set; }
[Required]
[DisplayName("First Name")]
public string FirstName { get; set; }
[Required(AllowEmptyStrings = true)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[DisplayName("Middle Name")]
public string MiddleName { get; set; }
[Required]
[DisplayName("Last Name")]
public string LastName { get; set; }
[Required]
[DisplayName("Email")]
public string Email { get; set; }
[Required(AllowEmptyStrings = true)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[DisplayName("Phone")]
public string Phone { get; set; }
[Required]
public DateOnly CreatedDate { get; set; } = DateOnly.FromDateTime(DateTime.Now);
[Required]
public DateOnly ModifiedDate { get; set; } = DateOnly.FromDateTime(DateTime.Now);
}
因为我想在一个视图中使用所有这三个模型,所以我为它们创建了一个 Register 视图模型:
public class Register
{
public List<SelectListItem> AccountTypes { get; set; }
public Account Account { get; set; }
public Person Person { get; set; }
}
这是服务于视图的相关控制器的 GET 操作方法:
public IActionResult Register()
{
var Register = new Register();
Register.AccountTypes = _db.AccountTypes.Select(accType => new SelectListItem
{
Value = accType.AccountTypeID.ToString(),
Text = accType.AccountTypeName
}).ToList();
return View(Register);
}
这里是注册视图本身:
@model Register
<form method="post">
<div class="row mb-2">
<div class="col-12">
<select asp-for="Account.AccountTypeID"
asp-items="Model.AccountTypes"
class="form-select"
aria-label="Default select example"
></select>
</div>
</div>
<div class="row mb-2">
<div class="col-12">
<label asp-for="Account.Username" class="d-block"></label>
<input asp-for="Account.Username" class="w-100" />
<span asp-validation-for="Account.Username" class="text-danger"></span>
</div>
<div class="col-12">
<label asp-for="Account.Password" class="d-block"></label>
<input asp-for="Account.Password" class="w-100" />
<span asp-validation-for="Account.Password" class="text-danger"></span>
</div>
</div>
<partial name="/Views/Person/_Create.cshtml" for="Person" />
<button type="submit" class="btn btn-primary">Create</button>
</form>
我现在正处于编写控制器 POST 操作方法的阶段,目的是将视图中的绑定字段提交到数据库中,我正在 运行 进行一些麻烦:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Register(Register Register)
{
if (ModelState.IsValid)
{
_db.Persons.Add(Register.Person);
_db.Accounts.Add(Register.Account);
_db.SaveChanges();
}
return View(Register);
}
当我在 ModelState.IsValid
行放置一个标记时,我看到它是 false
。我了解这是因为 3 个模型字段具有空值/无效值:
AccountTypes
Account.AccountType
Account.Person
我需要在 Register 模型中 AccountTypes
以在视图的下拉列表/select 字段中显示帐户类型列表。然后,Account.AccountType
和 Account.Person
也是必需的,因为它们建立了实体表之间的关系。所以,我想我无法从模型中删除这些字段。
我考虑过以某种方式将这些特定字段从验证过程中排除,同时将它们保留在模型中,但我不确定如何去做。此外,我有一种挥之不去的感觉,可能有更好的方法来处理我不知道的整个过程。
那么,让 ModelState 接受 POST 注册请求的最正确方法是什么?
在您的个人模型中,有几个字段标记为必填属性。由于此 ModelState 始终为 false,因为这些字段需要值并且视图或控制器(GET 调用)不提供 them.If 可能您可以删除需要的字段或为没有必填字段的人创建单独的模型。
然后要获得 selected AccountType,您需要像这样更改您的 Register 模型。
public class Register
{
public Register()
{
Person = new Person();
Account = new Account();
}
public int AccountTypeId { get; set; }
public List<SelectListItem> AccountTypes { get; set; }
public Account Account { get; set; }
public Person Person { get; set; }
}
并像这样在视图中更改 select。
<select asp-for="AccountTypeId"
asp-items="Model.AccountTypes"
class="form-select"
aria-label="Default select example"
></select>
显然您使用的是 net6。你有两个选择,让这个属性可以为空(也许更多)
public virtual AccountType? AccountType { get; set; }
public virtual Person? Person { get; set; }
或者为了防止以后出现这种情况 类 您可以从项目配置中删除 Nullable
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<!--<Nullable>enable</Nullable>-->
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>