自定义DataAnnotationsModelMetadataProvider无法正常工作

我有许多属性需要一个或多个validation属性,如下所示:

public class TestModel { [Some] [StringLength(6)] [CustomRequired] // more attributes... public string Truck { get; set; } } 

请注意以上所有注释都有效。

我不想一直写这个,因为每当应用Some时,所有其他属性也会应用于属性。 我希望能够做到这一点:

 public class TestModel { [Some] public string Truck { get; set; } } 

现在这可以通过inheritance来实现; 因此,我编写了一个自定义DataAnnotationsModelMetadataProvider并覆盖了CreateMetadata 。 这会查找使用Some修饰的任何内容,然后为其添加更多属性:

 public class TruckNumberMetadataProvider : DataAnnotationsModelMetadataProvider { protected override ModelMetadata CreateMetadata(IEnumerable attributes, Type containerType, Func modelAccessor, Type modelType, string propertyName) { var attributeList = attributes.ToList(); if (attributeList.OfType().Any()) { attributeList.Add(new StringLengthAttribute(6)); attributeList.Add(new CustomRequiredAttribute()); } return base.CreateMetadata(attributeList, containerType, modelAccessor, modelType, propertyName); } } 

这些属性有助于:

 public class CustomRequiredAttribute : RequiredAttribute { public CustomRequiredAttribute() { this.ErrorMessage = "Required"; } } public class SomeAttribute : RegularExpressionAttribute { public SomeAttribute() : base(@"^[1-9]\d{0,5}$") { } } 

用法

 @Html.TextBoxFor(x => x.Truck) 

HTML呈现:

   

问题/问题

  1. CustomRequired已应用。 但是,如果我使用CustomRequired ,它为什么会从基类RequiredAttribute获取消息。 data-val-required应该只说必需
  2. 不应用6个字符串的StringLenthStringLength没有任何渲染迹象,为什么?

您的自定义DataAnnotationsModelMetadataProvider正在做的是创建/修改与您的属性关联的ModelMetada

如果检查ModelMetadata类,您将注意到它包含诸如string DisplayNamestring DisplayFormatString类的属性,这些属性是根据[Display][DisplayFormat]属性的应用程序设置的。 它还包含一个bool IsRequired属性,用于确定是否需要属性的值(稍后再说一点)。

它不包含与正则表达式或最大长度相关的任何内容,或者实际上与validation相关的任何内容(除了IsRequired属性和用于validation值可以转换为typeModelType之外)。

HtmlHelper方法负责生成传递给视图的html。 要生成data-val-*属性, TextBoxFor()方法在内部调用HtmlHelper类的GetUnobtrusiveValidationAttributes()方法,该方法又调用DataAnnotationsModelValidatorProvider类中的方法,该方法最终生成data-val属性名称和使用的值的Dictionary 。生成HTML。

如果您需要更多细节(请参阅下面的链接),我将留给您探索源代码,但总结一下,它会获取应用于您的Truck属性的所有属性的集合,这些属性inheritance自ValidationAttribute以构建字典。 在您的情况下,唯一的ValidationAttribute[Some] ,它派生自RegularExpressionAttribute因此添加了data-val-regexdata-val-regex-pattern属性。

但是因为您已在TruckNumberMetadataProvider添加了CustomRequiredAttribute ,所以TruckNumberMetadataProviderIsRequired属性已设置为true 。 如果您使用DataAnnotationsModelValidatorProviderGetValidators() ,您将看到RequiredAttribute会自动添加到属性集合中,因为您尚未将属性应用于该属性。 相关的代码片段是

 if (AddImplicitRequiredAttributeForValueTypes && metadata.IsRequired && !attributes.Any(a => a is RequiredAttribute)) { attributes = attributes.Concat(new[] { new RequiredAttribute() }); } 

这导致data-val-required属性被添加到html中(它使用默认消息,因为它对CustomRequiredAttribute一无所知)

如果您想了解内部工作原理,可以使用源代码文件

  1. HtmlHelper.cs – 在第413行引用GetUnobtrusiveValidationAttributes()方法
  2. ModelValidatorProviders.cs – 获取用于validation的各种ValidatorProviders
  3. DataAnnotationsModelValidatorProvider.cs – ValidationAttributes的ValidatorProvider

一个可能的解决方案是,如果您真的只想使用一个ValidationAttribute就是让它实现IClientValidatable并在GetClientValidationRules()方法中添加规则,例如

 var rule = new ModelClientValidationRule { ValidationType = "required", ErrorMessage = "Required" } 

它将由ClientDataTypeModelValidatorProvider读取(并删除您的TruckNumberMetadataProvider类)。 但是,这会产生维护噩梦,因此我建议您只需将3个validation属性添加到您的属性中