Razor Pages - 由于多个对象共享参数,模型验证失败

Posted

技术标签:

【中文标题】Razor Pages - 由于多个对象共享参数,模型验证失败【英文标题】:Razor Pages - Model validation fails due to multiple objects sharing parameters 【发布时间】:2019-06-01 18:19:31 【问题描述】:

我遇到了一个我似乎无法解决的问题。我正在使用 Razor Pages 并且有两个可以绑定的对象。

[BindProperty]
public MeetingMinuteInputDto MeetingToCreate  get; set; 

[BindProperty]
public MeetingMinuteUpdateDto MeetingToUpdate  get; set; 

以上两个是单独的 dto,用于在我的数据库中创建/更新基本实体。我有两个单独的 dto,因为我只允许更新特定项目(以防止过度发布)。这两个类都有NameDateReminderMeetingMinuteUpdateDto 只允许更改 Date

Name 属性是必需的,不能为空。我在同一个页面/控制器中拥有两个对象的原因是因为我使用模式来创建/更新,我宁愿不创建多个页面只是为了创建/编辑对象。

我有两种用户可以填写的表格 - 一种用于编辑,另一种用于创建。每一个都将值绑定到一个特定的对象(即创建表单会将其发布的表单值绑定到MeetingMinuteInputDto)。

<div class="form-group">
    <div class="col-md-10">
          <label asp-for="MeetingToCreate.Name" class="control-label"></label>
          <input asp-for="MeetingToCreate.Name" class="form-control" />
          <span asp-validation-for="MeetingToCreate.Name" class="text-danger"></span>
      </div>
</div>

以上是我创建新会议的表单示例。我检查了提交表单时,只绑定了MeetingMinuteInputDto 值。另一个对象 (MeetingMinuteUpdateDto) 的所有属性都具有空值。

但是当我检查模型状态时,MVC 会在“名称”属性上抛出一个错误,指出它为空。我查看了模型状态的结果,并且有一个名为“Name”的键,它与任何未通过验证的对象无关。

如果我从页面中删除另一个对象(即我删除 MeetingMinuteUpdateDto)并进行模型绑定,那么一切正常。如何防止模型验证尝试验证与当前操作无关的对象?我希望 Create 操作仅验证创建对象,反之亦然。

我尝试过TryValidateModel(MeetingToCreate),但这也为模型验证提供了错误。

注意:我不能只将对象属性放在外面,因为我有其他需要执行此操作的页面,其中更新/创建对象有 10 多个共享/不共享属性.


更新 - 我可以手动从模型状态字典中删除验证错误。但我不太喜欢这种方法,因为我不想遍历所有属性以获取不正确的键并删除它们。


更新 2:以下是有关此问题发生时间的更多详细信息:

public class MeetingMinuteInputDto: MeetingMinuteManipulationDto
    
        public string AdditionalInfo  get; set; 
    

public class MeetingMinuteUpdateDto : MeetingMinuteManipulationDto
    
    

public class MeetingMinuteManipulationDto
    
        [Required]
        public string Name  get; set; 
        public IFormFile FileToUpload  get; set; 
    

在我的 Razor 页面中,这两个属性都带有 bind 属性:

public class MeetingMinutesModel : PageModel
    
        [BindProperty]
        public MeetingMinuteInputDto MeetingToCreate  get; set; 

        [BindProperty]
        public MeetingMinuteUpdateDto MeetingToUpdate  get; set;  
        //...stuff

我在创建/编辑页面上的表单如下所示:

<form asp-page="/MeetingMinute/MeetingMinutes" asp-page-handler="CreateMeeting">
    <div class="form-group">
        <div class="col-md-10">
              <label asp-for="MeetingToCreate.Name" class="control-label"></label>
              <input asp-for="MeetingToCreate.Name" class="form-control" />
              <span asp-validation-for="MeetingToCreate.Name" class="text-danger"></span>
          </div>
    </div>
    //other inputs and label for rest of the properties
   <button type="submit" class="btn btn-danger">Submit</button>
</form>

表单的输入设置为专门绑定到 MeetingToCreate 属性。当我提交表单时,表单值已正确绑定。但与此同时,MeetingToUpdate 属性出现 ModelState 错误(因为它与 MeetingToCreate 共享一些字段)。

我假设在 Razor 页面中,模型状态验证正在验证 MeetingMinutesModel 而不是单个 MeetingToCreate/MeetingToUpdate 属性。在 MVC 中,我认为这可能会有所不同,因为操作需要有参数,而不是 razor 页面中页面模型上的开放式属性。

【问题讨论】:

与其将这些作为属性添加到 PageModel,不如将它们作为 Create 和 Update 操作方法的参数。 @MikeBrind 我需要将它们作为公共参数,因为我需要在我的剃刀视图中访问它们。由于 razor 视图仅接受 razor 页面模型作为模型,如果它们是操作级别参数,我将如何访问视图中的这些值? 您能否分享可以重现该问题的更相关的代码? @XueliChen 详情请看我的最新更新 @DC_AC 没有找到任何允许我使用具有共享属性名称的模型的解决方案。我选择创建一个具有共享属性名称的类,以便它可以用于两个对象,具体取决于调用的页面操作。我相信整个页面本身就是正在验证的“模型”,而不是单个属性,但我必须深入研究源代码来确认这一点。 【参考方案1】:

我正在做类似的事情。搜索任何解决方案,我找到了您的问题,但没有解决方案我分享解决方案。

我实现了多个 BindProperty 和多个 Actions OnPost, 我找到的解决方案是在 Asp.Net MVC 中使用 [Bind] 属性做一些事情。

在你的情况下,它会是。

public class MeetingMinutesModel : PageModel

    //[BindProperty] remove it
    public MeetingMinuteInputDto MeetingToCreate  get; set; 

    //[BindProperty] remove it
    public MeetingMinuteUpdateDto MeetingToUpdate  get; set; 
    //...stuff


public IActionResult OnPost([Bind("Name, FileToUpload, AdditionalInfo")] MeetingMinuteInputDto MeetingToCreate)

    //Do somthing

【讨论】:

很好的解决方案!绝对可以解决。将相同的标识符声明为 PageModel 的属性和页面处理程序的参数并不理想。 (此外,拥有 PageModel 的属性只能“欺骗”AspNetCore)尽管如此,这是我找到的解决这个问题的最佳解决方案。完全符合我的要求 - 干得好!【参考方案2】:

在 Razor pages 中,页面中的 @model 是 PageModel 并且 MeetingMinuteManipulationDto 类的 "Name" 属性是 Required ,因此模型状态验证会验证所有PageModel 中的属性。 如果你坚持原来的想法,你可以尝试使用ViewComponent在同一剃刀视图中实现创建/编辑对象。

【讨论】:

嗨雪莉,我想可能是这样。使用视图组件创建/编辑对象是什么意思?我当然可以将表单放入视图组件并显示在 razor 视图中 - 但是当我发布表单时 - 它最终会进入 razor 页面并具有相同的模型绑定问题。在这种情况下,视图组件有什么帮助?

以上是关于Razor Pages - 由于多个对象共享参数,模型验证失败的主要内容,如果未能解决你的问题,请参考以下文章

如何匹配 Razor Pages 中的多个路由?

Razor Pages:在链接中传递查询参数

使用 Razor Pages 在路由中间定义所需的路径参数?

ASP.NET Razor Pages 路由参数命名页面

部分视图中的模型为空 - asp.net core razor pages

ASP .NET Core 5 Razor Pages:如何正确使用局部视图并验证其模型状态?