简体   繁体   English

使用TryUpdateModel绑定时,MVC ValidationSummary忽略模型级验证错误

[英]MVC ValidationSummary ignores model level validation errors when bound using TryUpdateModel

This is a very similar problem to one already posted here: ASP.NET MVC: Validation messages set in TryUpdateModel not showning ValidationSummary 这是一个与已发布的问题非常类似的问题: ASP.NET MVC:在TryUpdateModel中设置的验证消息未显示ValidationSummary

I'm not sure whether that old topic was in reference to an earlier version of MVC, but in MVC3 i'm experiencing some odd behaviour along similar lines. 我不确定那个旧主题是否参考了早期版本的MVC,但在MVC3中,我遇到了类似行的奇怪行为。

I have model class called Trade. 我有一个名为Trade的模型类。 This inherits from IValidatableObject, so therefore implements a Validate method. 它继承自IValidatableObject,因此实现了Validate方法。 Within this we have some validation of the model as a whole (as opposed to data annotations that enforce validation of properties). 在此范围内,我们对模型进行了整体验证(与强制验证属性的数据注释相对)。 The validation is as follows: 验证如下:

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

We have a view model to help with the display of a trade. 我们有一个视图模型来帮助显示交易。 This contains a reference to a trade model via a TradeModel property. 这包含通过TradeModel属性对交易模型的引用。 So basically the view model is a Trade model, plus some extra info for the population of drop-down lists such as Counterparties, etc. 所以基本上,视图模型是一个交易模型,加上一些额外信息,用于下拉列表的数量,例如交易对手等。

Our CSHTML class contains a ValidationSummary, with "true" as the argument, meaning that it will only show model errors. 我们的CSHTML类包含一个ValidationSummary,其中“true”作为参数,这意味着它只显示模型错误。

If i implement my HttpPost controller method for creating a new Trade as follows... 如果我实现我的HttpPost控制器方法来创建一个新的贸易如下...

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

...when i enter a trade with StartDate > EndDate, I am finding that TryUpdateModel returns false and the user is directed back to their trade. ...当我使用StartDate> EndDate进行交易时,我发现TryUpdateModel返回false并且用户被定向回他们的交易。 This seems logical. 这看似合乎逻辑。 Unfortunately ValidationSummary does not show any error messages. 不幸的是ValidationSummary没有显示任何错误消息。

If i put a breakpoint in the Create method and investigate the ModelState, i can see that there is an error message in the dictionary. 如果我在Create方法中放置一个断点并调查ModelState,我可以看到字典中有一条错误消息。 It is against a Key of "TradeModel", rather than against any of the properties. 它违反了“TradeModel”的密钥,而不是针对任何属性。 Again, this seems logical. 再次,这似乎合乎逻辑。

One theory as to why this is, is that ValidationSummary presumes that any validation errors against a key that is not String.Empty must be property validation errors, it's ignoring our validation errors because we have a view model that contains a reference to a model, therefore resulting in the Key being "TradeModel". 关于其原因的一个理论是ValidationSummary假定针对非String.Empty的键的任何验证错误必须是属性验证错误,它忽略了我们的验证错误,因为我们有一个包含对模型的引用的视图模型,因此导致Key成为“TradeModel”。

What blows this theory out of the water is this: if i rewrite the controller's Create function as follows... 这个理论脱离水的原因是:如果我重写控制器的Create函数如下......

  [HttpPost]
  public ActionResult Create(Trade trade, FormCollection collection)
  {
     if (this.SaveChanges(this.ModelState, trade))
     {
        return this.RedirectToAction("Index");
     }

     return this.View(trade);
  }

...and therefore rely on MVC performing the binding "automatically", and re-run the same test scenario, the user is presented with the intended error message! ...因此依赖MVC“自动”执行绑定,并重新运行相同的测试场景,向用户显示预期的错误消息!

If i add a breakpoint and look at ModelState, i'm seeing identical error messages against the same keys as before, but this time ValidationSummary picks them up! 如果我添加一个断点并查看ModelState,我会看到与之前相同的密钥的相同错误消息,但这次ValidationSummary会选择它们!

If i amend the validation as follows then it works with the controller's Create function written in either way: 如果我按如下方式修改验证,那么它适用于以任一方式编写的控制器的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;
  }

So clearly it's only an issue with model level validation errors. 很明显,这只是模型级验证错误的问题。

Any help with this would be greatly appreciated! 任何有关这方面的帮助将不胜感激! There are reasons (which i won't go into now) why we need to create the instance of our view model manually and call the binding using TryUpdateModel. 有些原因(我现在不会讨论)为什么我们需要手动创建视图模型的实例并使用TryUpdateModel调用绑定。

Thanks in advance! 提前致谢!

UPDATE UPDATE

It appears that the theory of ValidationSummary only displaying errors with a key in the ModelState of String.Empty when told to exclude property errors is actually true. 似乎ValidationSummary的理论仅在被告知要排除属性错误时在String.Empty的ModelState中显示错误,实际上是真的。 I've looked at the source code and it actually uses ViewData.TemplateInfo.HtmlFieldPrefix to find the validation errors at the model-level. 我查看了源代码,它实际上使用ViewData.TemplateInfo.HtmlFieldPrefix来查找模型级别的验证错误。 By default this is String.Empty. 默认情况下,这是String.Empty。

Changing this value to "TradeModel" therefore seems logical, but it causes every HTML id or name to be prefixed, so the binding then fails! 因此,将此值更改为“TradeModel”似乎是合乎逻辑的,但它会导致每个HTML ID或名称都带有前缀,因此绑定会失败!

If the view model contains a reference to a business model, any errors added to the ModelState by IValidatableObject are added with a key including a prefix equal to the business model property name in the view model (in our case "TradeModel"), resulting in keys such as "TradeModel.CounterpartyId", etc. Model-level errors are added with a key equal to the business model property name of the view model ("TradeModel"). 如果视图模型包含对业务模型的引用,则IValidatableObject添加到ModelState的任何错误都会添加一个键,该键包含一个前缀,该前缀等于视图模型中的业务模型属性名称(在我们的示例中为“TradeModel”),从而导致诸如“TradeModel.CounterpartyId”之类的密钥等。使用等于视图模型的业务模型属性名称的密钥(“TradeModel”)添加模型级错误。

So it appears that the business cannot add model-level validation errors if the view model is constructed in this way. 因此,如果以这种方式构建视图模型,业务似乎无法添加模型级验证错误。

What's puzzling me is why this does actually work in our "real" project when the controller's Create function is written so that it takes a Trade view model object as an argument. 让我感到困惑的是,当编写控制器的Create函数以便将Trade视图模型对象作为参数时,这实际上可以在我们的“真实”项目中工作。 I must have missed this yesterday, but looking at it today, when MVC binds and the validation is triggered, it appears to add an extra key at the end of the dictionary with a value of String.Empty. 我一定错过了昨天,但今天看看它,当MVC绑定并且触发验证时,它似乎在字典末尾添加了一个额外的键,其值为String.Empty。 This contains a duplicate of the errors added with a key of TradeModel. 这包含使用TradeModel的键添加的错误的副本。 As you'd expect, ValidationSummary then picks them up! 正如您所期望的那样,ValidationSummary然后选择它们!

So why does MVC do this in our live project, but not in the simple test app? 那么为什么MVC在我们的实时项目中这样做,而不是在简单的测试应用程序中呢?

I've seen validation triggered twice when the controller function is written to take the view model as an argument. 在编写控制器函数以将视图模型作为参数时,我已经看到两次验证被触发。 Maybe this is doing something subtly different each time? 也许这每次都有微妙的不同?

UPDATE...AGAIN UPDATE ... AGAIN

The reason it works in our real project is there is some code buried in our base controller (from which all others inherit) that copies all errors found in the model state to a new entry with a key of String.Empty. 它在我们的真实项目中工作的原因是我们的基本控制器中隐藏了一些代码(所有其他人都继承了这些代码),它将模型状态中发现的所有错误复制到具有String.Empty键的新条目。 This code was only being called in one out of the two scenarios. 此代码仅在两个方案中的一个中被调用。 So there is no actual difference between the real and test apps. 因此,真实应用和测试应用之间没有实际差异。

I now understand what's going in and why ValidationSummary is behaving like this. 我现在明白了究竟发生了什么以及为什么ValidationSummary会像这样表现。

Ok. 好。 I'm now at a point where i can answer this myself. 我现在正处于一个可以自己回答这个问题的地步。

If ValidationSummary is used with the ExcludePropertyErrors argument set to True, it will look for errors in the model state using a key equal to ViewData.TemplateInfo.HtmlFieldPrefix. 如果将ValidationSummary与ExcludePropertyErrors参数设置为True一起使用,它将使用等于ViewData.TemplateInfo.HtmlFieldPrefix的键在模型状态中查找错误。 By default this is String.Empty. 默认情况下,这是String.Empty。

If you have a view model which exposes your business model, something like this: 如果您有一个公开您的业务模型的视图模型,则如下所示:

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

When the validation takes place, errors (ValidationResults) that are added at the model-level (where there is no further argument to the ValidationResult constructor that takes the property name(s), are added to the ModelState with a prefix of the property name of the view model - in this example "TradeModel". 进行验证时,在模型级别添加的错误(ValidationResults)(其中没有进一步的ValidationResult构造函数的参数获取属性名称)将添加到具有属性名称前缀的ModelState中视图模型 - 在此示例中为“TradeModel”。

There are a few ways around this that we're currently considering. 我们正在考虑的方法有几种。

  1. Do not expose the business model outside the view model. 不要在视图模型之外公开业务模型。 This essentially involves binding to the view model, rather than to the business model of the view model. 这主要涉及绑定到视图模型,而不是绑定到视图模型的业务模型。 This could be done two ways: make the view model a subclass of the business model; 这可以通过两种方式完成:使视图模型成为业务模型的子类; or duplicate properties from business model to the view model. 或者从业务模型到视图模型的重复属性。 I prefer the former. 我更喜欢前者。
  2. Write code to copy model-level errors into a new ModelState dictionary key of String.Empty. 编写代码以将模型级错误复制到String.Empty的新ModelState字典键中。 Unfortunately this might not be possible to do elegantly since the property name of the view model that exposes the business model is what's used as the key. 遗憾的是,这可能无法优雅地完成,因为公开业务模型的视图模型的属性名称是用作密钥的。 This may be different per controller/view-model. 每个控制器/视图模型可能有所不同。
  3. Use the following in the view. 在视图中使用以下内容。 This will display error messages that are for the business model. 这将显示业务模型的错误消息。 Essentially this pretending that model-level errors for the business model are in fact property errors. 本质上,这假装商业模型的模型级错误实际上是属性错误。 The display of these is not the same as for ValidationSummary, but maybe this could be cured with CSS: 这些显示与ValidationSummary的显示不同,但也许这可以通过CSS来解决:

    @Html.ValidationMessageFor(m => m.TradeModel) @ Html.ValidationMessageFor(m => m.TradeModel)

  4. Subclass ValidationSummary. 子类ValidationSummary。 This would involve changing it so that it knows which keys in ModelState refer to business model properties of the view model. 这将涉及更改它,以便它知道ModelState中的哪些键引用视图模型的业务模型属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 MVC使用Controller中的TryUpdateModel形式检查表单中的模型值 - MVC Check model values in form using TryUpdateModel in Controller 当必填字段为空时,MVC ValidationSummary不显示错误 - MVC ValidationSummary not displaying errors when the required field is empty ASP.Net MVC使用具有多个ValidationSummary的AddModelError进行查询字符串验证 - ASP.Net MVC Using AddModelError with multiple ValidationSummary for querystring validation MVC模型验证和打印错误 - MVC model validation and printing errors 在MVC - 4中加载页面时是否可以显示验证错误(带模型验证) - Is it possible to Display validation errors when page loads in MVC - 4 (with model validations) mvc3 ValidationSummary排除属性错误IValidatableObject - mvc3 ValidationSummary exclude property errors IValidatableObject 非模型错误的Mimick验证摘要 - Mimick ValidationSummary For Non-Model Errors 在MVC的操作级别上跳过模型验证 - Skip Model validation at action level in MVC 如果错误已经逐字段显示,如何不显示ASP MVC ValidationSummary? - How to not display ASP MVC ValidationSummary in case when errors already displayed field by field? 使用TryUpdateModel时的asp.net核心MVC控制器单元测试 - asp.net core mvc controller unit testing when using TryUpdateModel
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM