自定义 DataAnnotationsModelMetadataProvider 不起作用 [英] Customized DataAnnotationsModelMetadataProvider not working
问题描述
我有许多属性需要 1 个或多个验证属性,如下所示:
公共类TestModel{[一些][字符串长度(6)][CustomRequired]//更多属性...公共字符串卡车{获取;放;}}
请注意以上所有注释都有效.
我不想一直这样写,因为每当应用 Some
时,所有其他属性也会应用到该属性.我希望能够做到这一点:
公共类TestModel{[一些]公共字符串卡车{获取;放;}}
现在可以通过继承来实现;因此,我编写了一个自定义 DataAnnotationsModelMetadataProvider
并覆盖了 CreateMetadata
.这会寻找任何用 Some
修饰的东西,然后给它添加更多的属性:
公共类 TruckNumberMetadataProvider : DataAnnotationsModelMetadataProvider{受保护的覆盖 ModelMetadata CreateMetadata(IEnumerable 属性,Type containerType,Func
这些是万一有帮助的属性:
公共类 CustomRequiredAttribute : RequiredAttribute{公共 CustomRequiredAttribute(){this.ErrorMessage = "必填";}}公共类 SomeAttribute :RegularExpressionAttribute{公共 SomeAttribute(): base(@"^[1-9]d{0,5}$"){}}
使用
@Html.TextBoxFor(x => x.Truck)
HTML 渲染:
</输入>
问题/疑问
- 应用了
CustomRequired
.但是,如果我使用CustomRequired
,它为什么会从基类RequiredAttribute
中获取消息.data-val-required
应该只是说Required. - 不应用 6 个字符的
StringLenth
.StringLength
没有任何渲染的迹象,为什么?
您的自定义 DataAnnotationsModelMetadataProvider
所做的是创建/修改与您的属性关联的 ModelMetada
.>
如果您检查 ModelMetadata 类你会注意到它包含诸如string DisplayName
和string DisplayFormatString
等属性,这些属性是根据[Display]
和 [DisplayFormat]
属性.它还包含一个 bool IsRequired
属性,用于确定是否需要属性的值(稍后会详细介绍).
它不包含任何与正则表达式或最大长度相关的内容,也不包含任何与验证相关的内容(除了 IsRequired
属性和用于验证该值是否可以转换为 type
).
HtmlHelper
方法负责生成传递给视图的 html.要生成 data-val-*
属性,您的 TextBoxFor()
方法在内部调用 HtmlHelper
GetUnobtrusiveValidationAttributes()
方法/code> 类依次调用 DataAnnotationsModelValidatorProvider
类中的方法,该类最终生成 data-val
属性名称和值的 Dictionary
生成html.
如果您想了解更多详细信息(请参阅下面的链接),我会将其留给您去探索源代码,但总而言之,它会获取应用于您的 Truck
属性的所有属性的集合,这些属性继承从 ValidationAttribute
构建字典.在您的情况下,唯一的 ValidationAttribute
是 [Some]
它派生自 RegularExpressionAttribute
所以 data-val-regex
和data-val-regex-pattern
属性被添加.
但是因为您已经在 TruckNumberMetadataProvider
中添加了 CustomRequiredAttribute
,ModelMetadata
的 IsRequired
属性已经被设置为 true
.如果您检查 DataAnnotationsModelValidatorProvider
的 GetValidators()
,您将看到 RequiredAttribute
自动添加到属性集合中,因为您尚未应用到物业.相关的代码片段是
if (AddImplicitRequiredAttributeForValueTypes && metadata.IsRequired && !attributes.Any(a => a is RequiredAttribute)){attributes = attributes.Concat(new[] { new RequiredAttribute() });}
这导致 data-val-required
属性被添加到 html(它使用默认消息,因为它对你的 CustomRequiredAttribute
一无所知)
如果您想了解内部工作原理,可帮助您入门的源代码文件
- HtmlHelper.cs- 参考第 413 行的
GetUnobtrusiveValidationAttributes()
方法 - ModelValidatorProviders.cs- 获取用于验证的各种 ValidatorProviders
- DataAnnotationsModelValidatorProvider.cs-
ValidationAttributes
的 ValidatorProvider
如果您真的只想使用一个 ValidationAttribute
,一种可能的解决方案是让它实现 IClientValidatable
并在 GetClientValidationRules()
中实现方法,添加规则,例如
var rule = new ModelClientValidationRule{ValidationType = "必填",错误信息 = "必需"}
将由 ClientDataTypeModelValidatorProvider
读取(并删除您的 TruckNumberMetadataProvider
类).但是,这会造成维护噩梦,因此我建议您只需将 3 个验证属性添加到您的属性中
I have many properties that require 1 or more validation attributes, like the following:
public class TestModel
{
[Some]
[StringLength(6)]
[CustomRequired] // more attributes...
public string Truck { get; set; }
}
Please note all the above annotations work.
I do not want to write that all the time because whenever Some
is applied, all other attributes are also applied to the property. I want to be able to do this:
public class TestModel
{
[Some]
public string Truck { get; set; }
}
Now this is doable by inheriting; therefore, I wrote a custom DataAnnotationsModelMetadataProvider
and overrode the CreateMetadata
. This looks for anything that is decorated with Some
and then adds more attributes to it:
public class TruckNumberMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var attributeList = attributes.ToList();
if (attributeList.OfType<SomeAttribute>().Any())
{
attributeList.Add(new StringLengthAttribute(6));
attributeList.Add(new CustomRequiredAttribute());
}
return base.CreateMetadata(attributeList, containerType, modelAccessor, modelType, propertyName);
}
}
These are the attributes in case it helps:
public class CustomRequiredAttribute : RequiredAttribute
{
public CustomRequiredAttribute()
{
this.ErrorMessage = "Required";
}
}
public class SomeAttribute : RegularExpressionAttribute
{
public SomeAttribute()
: base(@"^[1-9]d{0,5}$")
{
}
}
Usage
@Html.TextBoxFor(x => x.Truck)
HTML Rendered:
<input name="Truck" id="Truck" type="text" value=""
data-val-required="The Truck field is required."
data-val-regex-pattern="^[1-9]d{0,5}$"
data-val-regex="The field Truck must match the regular expression '^[1-9]d{0,5}$'."
data-val="true">
</input>
Problems/Questions
- The
CustomRequired
was applied. But why does it pick up the message from the base classRequiredAttribute
if I am usingCustomRequired
. Thedata-val-required
should just say Required. - The
StringLenth
of 6 chars is not applied. There is no sign of anything rendered forStringLength
, why?
What your custom DataAnnotationsModelMetadataProvider
is doing is creating/modifying the ModelMetada
associated with your property.
If you inspect the ModelMetadata class you will note that it contains properties such as string DisplayName
and string DisplayFormatString
which are set based on the application of the [Display]
and [DisplayFormat]
attributes. It also contains a bool IsRequired
attribute that determines if the value of a property is required (a bit more on that later).
It does not contain anything relating to a regular expression or the maximum length, or indeed anything related to validation (except the IsRequired
property and the ModelType
which is used to validate that that the value can be converted to the type
).
It is the HtmlHelper
methods that are responsible for generating the html that is passed to the view. To generate the data-val-*
attributes, your TextBoxFor()
method internally calls the GetUnobtrusiveValidationAttributes()
method of the HtmlHelper
class which in turn calls methods in the DataAnnotationsModelValidatorProvider
class which ultimately generate a Dictionary
of the data-val
attribute names and values used to generate the html.
I'll leave it to you to explore the source code if you want more detail (refer links below) but to summarize, it gets a collection of all attributes applied to your Truck
property that inherit from ValidationAttribute
to build the dictionary. In your case the only ValidationAttribute
is [Some]
which derives from RegularExpressionAttribute
so the data-val-regex
and data-val-regex-pattern
attributes are added.
But because you have added your CustomRequiredAttribute
in the TruckNumberMetadataProvider
, the IsRequired
property of the ModelMetadata
has been set to true
. If you insect the GetValidators()
of DataAnnotationsModelValidatorProvider
you will see that a RequiredAttribute
is automatically added to the collection of attributes because you have not applied one to the property. The relevant snippet of code is
if (AddImplicitRequiredAttributeForValueTypes && metadata.IsRequired && !attributes.Any(a => a is RequiredAttribute))
{
attributes = attributes.Concat(new[] { new RequiredAttribute() });
}
which results in the data-val-required
attribute being added to the html (and it uses the default message because it knows nothing about your CustomRequiredAttribute
)
Source code files to get you started if you want to understand the internal workings
- HtmlHelper.cs - refer
GetUnobtrusiveValidationAttributes()
methods at line 413 - ModelValidatorProviders.cs - gets the various ValidatorProviders used for validation
- DataAnnotationsModelValidatorProvider.cs - the ValidatorProvider for
ValidationAttributes
One possible solution if you really want to use just one single ValidationAttribute
would be to have it implement IClientValidatable
and in the GetClientValidationRules()
method, add rules, for example
var rule = new ModelClientValidationRule
{
ValidationType = "required",
ErrorMessage = "Required"
}
which will be read by the ClientDataTypeModelValidatorProvider
(and delete your TruckNumberMetadataProvider
class). However that will create a maintenance nightmare so I recommend you just add the 3 validation attributes to your property
这篇关于自定义 DataAnnotationsModelMetadataProvider 不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!