如何使用 IValidatableObject?
据我了解,IValidatableObject
用于以一种可以相互比较属性的方式验证对象。
我仍然希望有属性来验证各个属性,但我想在某些情况下忽略某些属性的失败。
我是否试图在下面的情况下错误地使用它?如果不是我该如何实施?
public class ValidateMe : IValidatableObject
{
[Required]
public bool Enable { get; set; }
[Range(1, 5)]
public int Prop1 { get; set; }
[Range(1, 5)]
public int Prop2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!this.Enable)
{
/* Return valid result here.
* I don't care if Prop1 and Prop2 are out of range
* if the whole object is not "enabled"
*/
}
else
{
/* Check if Prop1 and Prop2 meet their range requirements here
* and return accordingly.
*/
}
}
}
I understand that IValidatableObject
is used to validate an object in a way that lets one compare properties against each other.
I'd still like to have attributes to validate individual properties, but I want to ignore failures on some properties in certain cases.
Am I trying to use it incorrectly in the case below? If not how do I implement this?
public class ValidateMe : IValidatableObject
{
[Required]
public bool Enable { get; set; }
[Range(1, 5)]
public int Prop1 { get; set; }
[Range(1, 5)]
public int Prop2 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!this.Enable)
{
/* Return valid result here.
* I don't care if Prop1 and Prop2 are out of range
* if the whole object is not "enabled"
*/
}
else
{
/* Check if Prop1 and Prop2 meet their range requirements here
* and return accordingly.
*/
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
首先,感谢@paper1337为我指出了正确的资源......我没有注册,所以我不能投票给他,如果其他人读到了这篇文章,请这样做。
以下是如何完成我想要做的事情。
Validatable 类:
如果验证失败,使用 Validator.TryValidateProperty() 将会添加到结果集合中。如果没有失败的验证,则不会向结果集合中添加任何内容,这表明验证成功。
进行验证:
将
validateAllProperties
设置为 false 以使此方法正常工作非常重要。当validateAllProperties
为 false 时,仅检查具有[Required]
属性的属性。这允许IValidatableObject.Validate()
方法处理条件验证。First off, thanks to @paper1337 for pointing me to the right resources...I'm not registered so I can't vote him up, please do so if anybody else reads this.
Here's how to accomplish what I was trying to do.
Validatable class:
Using
Validator.TryValidateProperty()
will add to the results collection if there are failed validations. If there is not a failed validation then nothing will be add to the result collection which is an indication of success.Doing the validation:
It is important to set
validateAllProperties
to false for this method to work. WhenvalidateAllProperties
is false only properties with a[Required]
attribute are checked. This allows theIValidatableObject.Validate()
method handle the conditional validations.引用 Jeff Handley 关于使用验证器验证对象和属性的博客文章:
这表明您尝试执行的操作无法开箱即用,因为验证将在步骤#2 中中止。您可以尝试创建从内置属性继承的属性,并在执行正常验证之前专门检查启用的属性是否存在(通过接口)。或者,您可以将验证实体的所有逻辑放入
Validate
方法中。您还可以查看
Validator
类 此处Quote from Jeff Handley's Blog Post on Validation Objects and Properties with Validator:
This indicates that what you are attempting to do won't work out-of-the-box because the validation will abort at step #2. You could try to create attributes that inherit from the built-in ones and specifically check for the presence of an enabled property (via an interface) before performing their normal validation. Alternatively, you could put all of the logic for validating the entity in the
Validate
method.You also could take a look a the exact implemenation of
Validator
class here只需添加几点:
由于
Validate()
方法签名返回IEnumerable<>
,因此yield return
可用于延迟生成结果 - 如果某些验证检查是 IO,这会很有用或 CPU 密集型。另外,如果您使用
MVC ModelState
,您可以将验证结果失败转换为ModelState
条目,如下所示(如果您在 自定义模型绑定器):Just to add a couple of points:
Because the
Validate()
method signature returnsIEnumerable<>
, thatyield return
can be used to lazily generate the results - this is beneficial if some of the validation checks are IO or CPU intensive.Also, if you are using
MVC ModelState
, you can convert the validation result failures toModelState
entries as follows (this might be useful if you are doing the validation in a custom model binder):我实现了一个用于验证的通用抽象类
I implemented a general usage abstract class for validation
已接受答案的问题在于,它现在依赖于调用者来正确验证对象。我要么删除 RangeAttribute 并在 Validate 方法内进行范围验证,要么创建一个自定义属性子类 RangeAttribute,它将所需属性的名称作为构造函数的参数。
例如:
The problem with the accepted answer is that it now depends on the caller for the object to be properly validated. I would either remove the RangeAttribute and do the range validation inside the Validate method or I would create a custom attribute subclassing RangeAttribute that takes the name of the required property as an argument on the constructor.
For example:
使用 IValidatableObject 或属性级验证(属性)实现验证逻辑,而不是使用 System.ComponentModel.DataAnnotations.Validator 类,这样
任何错误都应出现在验证集合中(ErrorMessage 属性不应为空)。
https://learn. microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validator?view=net-6.0
Implement validation logic using IValidatableObject or property level validation (attributes) than use System.ComponentModel.DataAnnotations.Validator class like this
any errors should be present in validations collection (ErrorMessage property should be not empty).
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.validator?view=net-6.0
你可以让它变得更简单。
假设我们有两个属性:经度和纬度。它们都是必需的,但仅当其中之一被提交时才需要。(如果我们填写经度,我们还需要提供纬度,反之亦然)
从类中的 IValidatableObject 继承并实现 >validate 方法
如果您验证 MyLocationModel 的实例,规则将自动应用:)
完整说明 此处。
You can make it so much simpler.
Say we have two properties: longitude and latitude. They are both required but only if one of them is filed in. (If we fill in longitude we need to provide a latitude as well and visa versa)
Inherit from IValidatableObject in your class and implement the validate method
The rules will automatically apply if you validate an instance of the MyLocationModel :)
Full explanation here.
我喜欢 cocogza 的答案,除了调用 base.IsValid 导致堆栈溢出异常,因为它会再次重新进入 IsValid 方法又来了。因此,我将其修改为特定类型的验证,在我的例子中,它是针对电子邮件地址的。
这样效果好多了!它不会崩溃并产生一条很好的错误消息。希望这对某人有帮助!
I liked cocogza's answer except that calling base.IsValid resulted in a stack overflow exception as it would re-enter the IsValid method again and again. So I modified it to be for a specific type of validation, in my case it was for an e-mail address.
This works much better! It doesn't crash and produces a nice error message. Hope this helps someone!
iValidate 有几个弱点。第一个是在所有其他验证之后运行,因此在修复模型验证错误之前您甚至不会看到结果。
此外,由于它是模型的一部分,因此每次将数据复制到新模型时以及仅显示尚未编辑的数据时,它都会针对数据集中的每一行运行。
您可以创建自定义注释来执行与 iValidate 相同的操作,它们会将结果添加到模型状态中。但是,我建议您简单地创建一个函数并将所有保存验证代码放入其中。或者,对于网站,您可以在创建模型后在控制器中进行“特殊”验证。例子:
iValidate has a couple of weaknesses. The first is that is runs AFTER all other validation, so you won't even see the results until you fix model validation errors.
Additionally, since it is part of the model, it runs every time you copy data into a new model, and for every row in the dataset when merely displaying data you haven't edited.
You could create custom annotations to do the same thing as iValidate and they would add the results to modelstate. However, I would suggest you simply create a function and place all your save validation code in that. Alternately, for websites, you could have your "special" validation in the controller after the model is created. Example: