gpt4 book ai didi

c# - 当使用TryUpdateModel绑定(bind)时,MVC ValidationSummary会忽略模型级别的验证错误

转载 作者:太空狗 更新时间:2023-10-29 21:40:41 29 4
gpt4 key购买 nike

这与已经在此处发布的问题非常相似:ASP.NET MVC: Validation messages set in TryUpdateModel not showning ValidationSummary

我不确定那个旧主题是否与MVC的早期版本有关,但是在MVC3中,我遇到了类似的奇怪现象。

我有一个称为“贸易”的模型课。这是从IValidatableObject继承的,因此实现了Validate方法。在其中,我们对模型进行了整体验证(与强制执行属性验证的数据注释相反)。验证如下:

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var validationResults = new List<ValidationResult>();

if (this.EndDate < this.StartDate)
{
validationResults.Add(new ValidationResult("End date must be greater than start date"));
}

return validationResults;
}

我们有一个 View 模型来帮助显示交易。它包含通过TradeModel属性对交易模型的引用。因此,基本上, View 模型是交易模型,再加上一些有关下拉列表(如交易对手)等附加信息。

我们的CSHTML类包含一个ValidationSummary,带有“true”作为参数,这意味着它将仅显示模型错误。

如果我实现我的HttpPost Controller 方法来创建新的交易,如下所示...
  [HttpPost]
public ActionResult Create(FormCollection collection)
{
var trade = new Trade();

if (this.TryUpdateModel(trade))
{
if (this.SaveChanges(this.ModelState, trade))
{
return this.RedirectToAction("Index");
}
}

return this.View(trade);
}

...当我使用StartDate> EndDate输入交易时,我发现TryUpdateModel返回false,并且用户被引导回他们的交易。这似乎合乎逻辑。不幸的是,ValidationSummary没有显示任何错误消息。

如果我在Create方法中放置一个断点并调查ModelState,则可以看到字典中有错误消息。它针对“TradeModel”的键,而不针对任何属性。同样,这似乎合乎逻辑。

关于此原因的一种理论是,ValidationSummary假定针对不是String的键的任何验证错误。Empty必须是属性验证错误,它会忽略我们的验证错误,因为我们有一个 View 模型,其中包含对模型的引用,因此,导致 key 为“TradeModel”。

令这一理论震惊的是:如果我按如下方式重写 Controller 的Create函数...
  [HttpPost]
public ActionResult Create(Trade trade, FormCollection collection)
{
if (this.SaveChanges(this.ModelState, trade))
{
return this.RedirectToAction("Index");
}

return this.View(trade);
}

...因此依赖于MVC“自动”执行绑定(bind),然后重新运行相同的测试方案,从而向用户显示预期的错误消息!

如果我添加一个断点并查看ModelState,我将看到与以前相同的针对相同键的错误消息,但是这次ValidationSummary会选择它们!

如果我按以下方式修改验证,则它可以与以任何一种方式编写的 Controller 的Create函数一起使用:
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var validationResults = new List<ValidationResult>();

if (this.EndDate < this.StartDate)
{
validationResults.Add(new ValidationResult("End date must be greater than start date", new[] { "StartDate" }));
}

return validationResults;
}

显然,这只是模型级验证错误的问题。

任何帮助,将不胜感激!有一些原因(我现在不再讨论)为什么我们需要手动创建 View 模型的实例并使用TryUpdateModel调用绑定(bind)。

提前致谢!

更新

看来ValidationSummary的理论只显示String.Empty的ModelState中带有键的错误,当被告知排除属性错误时,实际上是正确的。我看过源代码,它实际上使用ViewData.TemplateInfo.HtmlFieldPrefix在模型级别查找验证错误。默认情况下,这是String.Empty。

因此,将此值更改为“TradeModel”似乎是合乎逻辑的,但是这会导致每个HTML ID或名称都带有前缀,因此绑定(bind)将失败!

如果 View 模型包含对业务模型的引用,则通过IValidatableObject添加到ModelState的任何错误都将添加一个键,该键包含等于 View 模型中的业务模型属性名称的前缀(在我们的示例中为“TradeModel”),从而导致键,例如“TradeModel.CounterpartyId”等。使用等于 View 模型的业务模型属性名称的键(“TradeModel”)添加模型级错误。

因此,如果以这种方式构造 View 模型,则企业似乎无法添加模型级验证错误。

令我感到困惑的是,为什么当编写 Controller 的Create函数,使其以Trade view模型对象作为参数时,这种方法在我们的“真实”项目中确实起作用。我昨天一定错过了,但是今天看一下,当MVC绑定(bind)并触发验证时,它似乎在字典的末尾添加了一个额外的键,其值为String.Empty。其中包含与TradeModel关键字一起添加的错误的副本。如您所料,ValidationSummary然后将它们接起来!

那么,为什么MVC在我们的实时项目中而不是在简单的测试应用程序中这样做呢?

我已经看到在编写 Controller 函数以将 View 模型作为参数时触发了两次验证。也许这每次都在做一些细微的不同?

更新...再次

它在我们的实际项目中起作用的原因是在我们的基本 Controller 中埋藏了一些代码(其他所有代码都继承自该代码),该代码将在模型状态下发现的所有错误复制到具有String.Empty键的新条目中。仅在两种情况中的一种中调用了此代码。因此,实际应用程序和测试应用程序之间没有实际差异。

我现在了解发生了什么以及为什么ValidationSummary表现为这种方式。

最佳答案

好的。我现在可以自己回答这个问题。

如果在将ExcludePropertyErrors参数设置为True的情况下使用ValidationSummary,它将使用等于ViewData.TemplateInfo.HtmlFieldPrefix的键在模型状态下查找错误。默认情况下,这是String.Empty。

如果您有一个可以公开您的业务模型的 View 模型,则类似于以下内容:

namespace ValidationSummary.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

public class TradeModel : IValidatableObject
{
public DateTime StartDate { get; set; }

public DateTime EndDate { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> validationResults = new List<ValidationResult>();

if (EndDate < StartDate)
{
validationResults.Add(new ValidationResult("End date must not be before start date"));
}

return validationResults;
}
}
}

namespace ValidationSummary.ViewModels
{
public class Trade
{
public Trade()
{
this.TradeModel = new Models.TradeModel();
}

public Models.TradeModel TradeModel { get; private set; }
}
}

进行验证时,将在模型级别添加的错误(ValidationResults)(其中没有使用属性名称的ValidationResult构造函数的其他参数)将添加到ModelState中,并带有属性名称的前缀 View 模型的示例-在此示例中为“TradeModel”。

目前,我们正在考虑几种解决方法。
  • 不要在 View 模型之外公开业务模型。这实质上涉及到绑定(bind)到 View 模型,而不是绑定(bind)到 View 模型的业务模型。这可以通过两种方式完成:使 View 模型成为业务模型的子类;使 View 模型成为业务模型的子类。或将属性从业务模型复制到 View 模型。我更喜欢前者。
  • 编写代码以将模型级错误复制到String.Empty的新ModelState字典键中。不幸的是,这可能无法做到优雅,因为公开业务模型的 View 模型的属性名称是用作键的。每个 Controller / View 模型可能有所不同。
  • 在 View 中使用以下内容。这将显示针对业务模型的错误消息。从本质上讲,这假装业务模型的模型级错误实际上是属性错误。这些显示与ValidationSummary不同,但是也许可以使用CSS来解决:

    @ Html.ValidationMessageFor(m => m.TradeModel)
  • 子类ValidationSummary。这将涉及更改它,以便它知道ModelState中的哪些键引用 View 模型的业务模型属性。
  • 关于c# - 当使用TryUpdateModel绑定(bind)时,MVC ValidationSummary会忽略模型级别的验证错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11331303/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com