【问题标题】:ASP.NET Core 6 MVC - EF Core 6 - model is not validating correctlyASP.NET Core 6 MVC - EF Core 6 - 模型未正确验证
【发布时间】:2022-01-21 16:26:27
【问题描述】:

我目前正在尝试使用 ASP.NET Core 6 MVC 和 Entity Framework Core 6 和 npgsql 启动一个新项目。

当我尝试添加一个具有外国身份的实体时,ModelState.IsValid 一直返回 false - 因为模型不会扩展外国实体。

基本上我遵循了官方文档:

所以我的课程看起来像:

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);
        }
    }
}

当我现在填写表格并发布 StudiengangName=Test1234;FakultaetID=1(当然是现有的 FakultaetID = 1)时,我的模型看起来像这样:

StudiengangID = 0
StudiengangName = "Test1234"
Fakultaet = null
FakultaetID = 1

因此ModelState.IsValid 返回false,因为Fakultaetnull

在这里,我假设 EF Core 6 发挥了魔力并解析了我所引用的实体。

如果我在检查模型是否有效之前添加以下 sn-p,一切似乎都正常:

studiengang.Fakultaet =
                _context.Fakultaeten.SingleOrDefault(stg => stg.FakultaetID == studiengang.FakultaetID);
            ModelState.ClearValidationState(nameof(Fakultaet));
            TryValidateModel(studiengang);

但这似乎是一个肮脏的解决方法,因为在具有几乎相同设置的 .NET Core 3.1 中没有必要这样做。

有人知道我错过了什么吗?

【问题讨论】:

  • 据我所知,添加外键关系也不会自动扩展早期 EF 核心版本中的导航属性。
  • “解决方法”在SingleOrDefault 之前是Include(stg =&gt; stg.Fakultaet),但我觉得——在这个问题中——你正在寻找一种自动扩展它的方法。也许是virtual 属性?不确定
  • @BrettCaswell 已经尝试将其作为虚拟。 SingleOrDefault 已经是填写 Fakultaet 的有效解决方法。我想知道为什么以这种方式记录它并且 - 在 .NETcore 3.1 中工作
  • hmm.. 我明白了.. 是的,我会避免这样做 SingleOrDefault 部分。尝试将[Required] 属性添加到导航属性Fakultaet。 (例如,the-instructor-navigation-property

标签: c# asp.net-core entity-framework-core npgsql


【解决方案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>

最好在 viewModel 中添加一个 DDL 项列表

 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);
}

恕我直言,从发布操作中删除绑定

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddStudiengangInline(Studiengang studiengang )

最后是一个选择控件

 <select asp-for="FakultaetID" class="form-control" asp-items="@Model.FakultaetItems"> </select>

【讨论】:

  • 表单本身在父视图中,我从上面注入了部分。验证本身正在工作。尽管ModelState 无效,因为 EF 没有将 DropDown 中的 ID 引用到实体。
  • @rurcoasteagle 我更新了我的答案
【解决方案2】:

正如this document所说:

从 .NET 6 开始,新项目包括 &lt;Nullable&gt;enable&lt;/Nullable&gt; 项目文件中的元素。一旦 打开功能,现有的引用变量声明变为 不可为空的引用类型

在 .NET 6 中,不可为空的属性必须是必需的,否则 ModelState 将无效。

为了满足您的要求,您可以从项目文件中删除&lt;Nullable&gt;enable&lt;/Nullable&gt;

第二种方式,你可以像下面这样初始化模型:

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; }
}

最后一种方法就像您在模型验证中删除密钥一样。

【讨论】:

  • ?-one 成功了。稍后将尝试为空。创建Fakultaet 的空对象导致Fakultaet.FakuName 无效,因为我没有明确设置。
【解决方案3】:

尝试使用仅包含 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
        }

【讨论】:

    猜你喜欢
    • 2016-10-10
    • 2022-08-23
    • 2016-09-10
    • 2016-10-10
    • 2022-10-07
    • 2022-07-21
    • 2022-01-25
    • 2022-10-04
    • 2018-03-04
    相关资源
    最近更新 更多