ASP.NET Core 6 MVC - EF Core 6 - 模型未正确验证
ASP.NET Core 6 MVC - EF Core 6 - model is not validating correctly
我目前正在尝试使用 ASP.NET Core 6 MVC 和 Entity Framework Core 6 和 npgsql 开始一个新项目。
当我尝试添加一个具有外国身份的实体时,ModelState.IsValid
一直返回 false - 因为模型没有扩展外国实体。
基本上我遵循了官方文档:
- https://docs.microsoft.com/de-de/aspnet/core/data/ef-mvc/complex-data-model?view=aspnetcore-6.0
- https://docs.microsoft.com/de-de/aspnet/core/data/ef-mvc/update-related-data?view=aspnetcore-6.0
所以我的 类 看起来像:
namespace PV.Models
{
public class Fakultaet
{
[Key]
public int FakultaetID { get; set; }
[Required]
public string FakuName { get; set; }
}
public class Studiengang
{
[Key]
public int StudiengangID { get; set; }
[Required]
public string StudiengangName { get; set;}
[Required,ForeignKey("Fakultaet")]
public int FakultaetID { get; set; }
public Fakultaet Fakultaet { get; set; }
}
}
局部视图:
@model PV.Models.Studiengang
<tr>
<td>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input asp-for="StudiengangName" class="form-control" />
<span asp-validation-for="StudiengangName" class="text-danger"></span>
</td>
<td>
<select asp-for="FakultaetID" class="form-control" asp-items="ViewBag.FakultaetId">
<option disabled="disabled" selected="selected" value="0">Bitte wählen...</option>
</select>
<span asp-validation-for="FakultaetID" class="text-danger"></span>
</td>
<td>
<input type="submit" value="Speichern" class="btn btn-outline-success btn-sm" id="btn-addinline-submit" />
<input type="reset" onClick="location.reload()" class="btn btn-outline-danger btn-sm" id="btn-addinline-abort" value="Abbrechen" />
</td>
</tr>
控制器:
namespace PV.Controllers
{
public class StudiengangController : Controller
{
private readonly PraktikumsKontext _context;
public StudiengangController(PraktikumsKontext ctx)
{
_context = ctx;
}
// --- snip ---
// GET: Student/Add
public IActionResult AddStudiengangInline()
{
ViewBag.FakultaetId = new SelectList(_context.Fakultaeten.AsNoTracking(), "FakultaetID", "FakuName");
return PartialView();
}
// POST: Student/Add
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddStudiengangInline([Bind("StudiengangName, FakultaetID")] Studiengang studiengang )
{
if (ModelState.IsValid)
{
_context.Add(studiengang);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewData["FakultaetId"] = new SelectList(_context.Fakultaeten, "FakultaetID", "FakuName", studiengang.FakultaetID);
return PartialView(studiengang);
}
}
}
当我现在填写我的表格和 POST StudiengangName=Test1234;FakultaetID=1
(当然有 Fakultaet
和 ID = 1
)时,我的模型看起来像这样:
StudiengangID = 0
StudiengangName = "Test1234"
Fakultaet = null
FakultaetID = 1
因此 ModelState.IsValid
returns false
因为 Fakultaet
是 null
.
这里我假设 EF Core 6 发挥了它的魔力并解析了我引用的实体。
如果我在检查模型是否有效之前添加以下代码片段,似乎一切正常:
studiengang.Fakultaet =
_context.Fakultaeten.SingleOrDefault(stg => stg.FakultaetID == studiengang.FakultaetID);
ModelState.ClearValidationState(nameof(Fakultaet));
TryValidateModel(studiengang);
但这似乎是一个肮脏的解决方法,因为在具有几乎相同设置的 .NET Core 3.1 中没有必要。
有人知道我错过了什么吗?
您必须在视图中添加具有验证和防伪功能的表单
@model PV.Models.Studiengang
<form method="post" asp-controller="Studiengang" asp-action="AddStudiengangInline" >
@Html.AntiForgeryToken()
@Html.ValidationSummary(false, "", new { @class = "text-danger" })
<tr>
,,,,,
<td>
<input type="submit" value="Speichern" class="btn btn-outline-success btn-sm" id="btn-addinline-submit" />
...
</td>
</tr>
</form>
最好将DDL项列表添加到viewModel
public class Studiengang
{
....
public int FakultaetID { get; set; }
public Fakultaet Fakultaet { get; set; }
[NotMapped]
public List<SelectListItem> FakultaetItems { get; set; }
}
动作
public IActionResult AddStudiengangInline()
{
var model= new Studiengang { FakultaetItems = new _context.Fakultaeten
.Select (i=> new SelectListItem { Value=i.FakultaetID, Text=i.FakuName}).ToList();
return PartialView(model);
}
恕我直言,从 post 操作中删除绑定
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddStudiengangInline(Studiengang studiengang )
最后 select 控制
<select asp-for="FakultaetID" class="form-control" asp-items="@Model.FakultaetItems"> </select>
正如this document所说:
Beginning with .NET 6, new projects include the
<Nullable>enable</Nullable>
element in the project file. Once the
feature is turned on, existing reference variable declarations become
non-nullable reference types.
在 .NET 6 中,不可为空 属性 必须是必需的,否则 ModelState 将无效。
为达到您的要求,您可以从项目文件中删除 <Nullable>enable</Nullable>
。
第二种方式,您可以像下面这样初始化模型:
public class Studiengang
{
[Key]
public int StudiengangID { get; set; }
[Required]
public string StudiengangName { get; set;}
[Required,ForeignKey("Fakultaet")]
public int FakultaetID { get; set; }
public Fakultaet Fakultaet { get; set; } = new Fakultaet();
}
第三种方式,可以加?
允许为空:
public class Studiengang
{
[Key]
public int StudiengangID { get; set; }
[Required]
public string StudiengangName { get; set;}
[Required,ForeignKey("Fakultaet")]
public int FakultaetID { get; set; }
public Fakultaet? Fakultaet { get; set; }
}
最后一种方法就像您在模型验证中删除密钥所做的那样。
尝试使用仅包含从数据库填充的 StudentId、StudentName、FacultyId 和所有院系的集合作为属性的 InputModel,并将该模型传递给 dbContext。例如:
public class InputModel
{
public StudentId {get;set;}
public StudentName {get;set;}
public FacultyId {get;set;}
public ICollection<Faculty> Faculties {get;set;}
}
在控制器中你应该有两个动作:
public async Task<IActionResult> AddStudiengangInline()
{
var model = new InputModel()
{
Faculties = this.data.Faculties.toList() //or any filtering and stuff
}
return PartialView(model);
}
然后像您一样进行第二个操作,但它将接受第一个操作的模型:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddStudiengangInline(InputModel model)
{
if (ModelState.IsValid)
{
do Stuff
}
do stuff
}
我目前正在尝试使用 ASP.NET Core 6 MVC 和 Entity Framework Core 6 和 npgsql 开始一个新项目。
当我尝试添加一个具有外国身份的实体时,ModelState.IsValid
一直返回 false - 因为模型没有扩展外国实体。
基本上我遵循了官方文档:
- https://docs.microsoft.com/de-de/aspnet/core/data/ef-mvc/complex-data-model?view=aspnetcore-6.0
- https://docs.microsoft.com/de-de/aspnet/core/data/ef-mvc/update-related-data?view=aspnetcore-6.0
所以我的 类 看起来像:
namespace PV.Models
{
public class Fakultaet
{
[Key]
public int FakultaetID { get; set; }
[Required]
public string FakuName { get; set; }
}
public class Studiengang
{
[Key]
public int StudiengangID { get; set; }
[Required]
public string StudiengangName { get; set;}
[Required,ForeignKey("Fakultaet")]
public int FakultaetID { get; set; }
public Fakultaet Fakultaet { get; set; }
}
}
局部视图:
@model PV.Models.Studiengang
<tr>
<td>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input asp-for="StudiengangName" class="form-control" />
<span asp-validation-for="StudiengangName" class="text-danger"></span>
</td>
<td>
<select asp-for="FakultaetID" class="form-control" asp-items="ViewBag.FakultaetId">
<option disabled="disabled" selected="selected" value="0">Bitte wählen...</option>
</select>
<span asp-validation-for="FakultaetID" class="text-danger"></span>
</td>
<td>
<input type="submit" value="Speichern" class="btn btn-outline-success btn-sm" id="btn-addinline-submit" />
<input type="reset" onClick="location.reload()" class="btn btn-outline-danger btn-sm" id="btn-addinline-abort" value="Abbrechen" />
</td>
</tr>
控制器:
namespace PV.Controllers
{
public class StudiengangController : Controller
{
private readonly PraktikumsKontext _context;
public StudiengangController(PraktikumsKontext ctx)
{
_context = ctx;
}
// --- snip ---
// GET: Student/Add
public IActionResult AddStudiengangInline()
{
ViewBag.FakultaetId = new SelectList(_context.Fakultaeten.AsNoTracking(), "FakultaetID", "FakuName");
return PartialView();
}
// POST: Student/Add
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddStudiengangInline([Bind("StudiengangName, FakultaetID")] Studiengang studiengang )
{
if (ModelState.IsValid)
{
_context.Add(studiengang);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewData["FakultaetId"] = new SelectList(_context.Fakultaeten, "FakultaetID", "FakuName", studiengang.FakultaetID);
return PartialView(studiengang);
}
}
}
当我现在填写我的表格和 POST StudiengangName=Test1234;FakultaetID=1
(当然有 Fakultaet
和 ID = 1
)时,我的模型看起来像这样:
StudiengangID = 0
StudiengangName = "Test1234"
Fakultaet = null
FakultaetID = 1
因此 ModelState.IsValid
returns false
因为 Fakultaet
是 null
.
这里我假设 EF Core 6 发挥了它的魔力并解析了我引用的实体。
如果我在检查模型是否有效之前添加以下代码片段,似乎一切正常:
studiengang.Fakultaet =
_context.Fakultaeten.SingleOrDefault(stg => stg.FakultaetID == studiengang.FakultaetID);
ModelState.ClearValidationState(nameof(Fakultaet));
TryValidateModel(studiengang);
但这似乎是一个肮脏的解决方法,因为在具有几乎相同设置的 .NET Core 3.1 中没有必要。
有人知道我错过了什么吗?
您必须在视图中添加具有验证和防伪功能的表单
@model PV.Models.Studiengang
<form method="post" asp-controller="Studiengang" asp-action="AddStudiengangInline" >
@Html.AntiForgeryToken()
@Html.ValidationSummary(false, "", new { @class = "text-danger" })
<tr>
,,,,,
<td>
<input type="submit" value="Speichern" class="btn btn-outline-success btn-sm" id="btn-addinline-submit" />
...
</td>
</tr>
</form>
最好将DDL项列表添加到viewModel
public class Studiengang
{
....
public int FakultaetID { get; set; }
public Fakultaet Fakultaet { get; set; }
[NotMapped]
public List<SelectListItem> FakultaetItems { get; set; }
}
动作
public IActionResult AddStudiengangInline()
{
var model= new Studiengang { FakultaetItems = new _context.Fakultaeten
.Select (i=> new SelectListItem { Value=i.FakultaetID, Text=i.FakuName}).ToList();
return PartialView(model);
}
恕我直言,从 post 操作中删除绑定
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddStudiengangInline(Studiengang studiengang )
最后 select 控制
<select asp-for="FakultaetID" class="form-control" asp-items="@Model.FakultaetItems"> </select>
正如this document所说:
Beginning with .NET 6, new projects include the
<Nullable>enable</Nullable>
element in the project file. Once the feature is turned on, existing reference variable declarations become non-nullable reference types.
在 .NET 6 中,不可为空 属性 必须是必需的,否则 ModelState 将无效。
为达到您的要求,您可以从项目文件中删除 <Nullable>enable</Nullable>
。
第二种方式,您可以像下面这样初始化模型:
public class Studiengang
{
[Key]
public int StudiengangID { get; set; }
[Required]
public string StudiengangName { get; set;}
[Required,ForeignKey("Fakultaet")]
public int FakultaetID { get; set; }
public Fakultaet Fakultaet { get; set; } = new Fakultaet();
}
第三种方式,可以加?
允许为空:
public class Studiengang
{
[Key]
public int StudiengangID { get; set; }
[Required]
public string StudiengangName { get; set;}
[Required,ForeignKey("Fakultaet")]
public int FakultaetID { get; set; }
public Fakultaet? Fakultaet { get; set; }
}
最后一种方法就像您在模型验证中删除密钥所做的那样。
尝试使用仅包含从数据库填充的 StudentId、StudentName、FacultyId 和所有院系的集合作为属性的 InputModel,并将该模型传递给 dbContext。例如:
public class InputModel
{
public StudentId {get;set;}
public StudentName {get;set;}
public FacultyId {get;set;}
public ICollection<Faculty> Faculties {get;set;}
}
在控制器中你应该有两个动作:
public async Task<IActionResult> AddStudiengangInline()
{
var model = new InputModel()
{
Faculties = this.data.Faculties.toList() //or any filtering and stuff
}
return PartialView(model);
}
然后像您一样进行第二个操作,但它将接受第一个操作的模型:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddStudiengangInline(InputModel model)
{
if (ModelState.IsValid)
{
do Stuff
}
do stuff
}