自定义DataAnnotationsModelMetadataProvider无法正常工作

时间:2018-02-09 21:46:19

标签: c# asp.net-mvc razor asp.net-mvc-5

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

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

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

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

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

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

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

这些属性有助于:

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呈现:

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

问题/问题

  1. 已应用CustomRequired。但是,如果我使用RequiredAttribute,为什么它会从基类CustomRequired中获取消息。 data-val-required应该说必需
  2. 未应用6个字符StringLenth。没有为StringLength呈现任何内容的迹象,为什么?

1 个答案:

答案 0 :(得分:3)

您的自定义DataAnnotationsModelMetadataProvider正在做的是创建/修改与您的媒体相关联的ModelMetada

如果您检查ModelMetadata课程,则会注意到它包含string DisplayNamestring DisplayFormatString等属性,这些属性是根据[Display]和{{的应用程序设置的1}}属性。它还包含一个[DisplayFormat]属性,用于确定是否需要属性的值(稍后会更多)。

它不包含任何与正则表达式或最大长度相关的内容,或者实际上与验证相关的任何内容(bool IsRequired属性除外,IsRequired用于验证值是否可以转换为ModelType)。

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

如果您需要更多详细信息(请参阅下面的链接),我将留给您探索源代码,但总结一下,它会获取应用于您继承自data-val属性的所有属性的集合。 Truck来构建字典。在您的情况下,唯一的ValidationAttributeValidationAttribute,其源自[Some],因此会添加RegularExpressionAttributedata-val-regex属性。

但是因为您已在data-val-regex-pattern中添加了CustomRequiredAttributeTruckNumberMetadataProvider的{​​{1}}属性已设置为IsRequired。如果您发现ModelMetadata true,您会看到GetValidators()会自动添加到属性集合中,因为您尚未向该属性应用DataAnnotationsModelValidatorProvider。相关的代码片段是

RequiredAttribute

导致if (AddImplicitRequiredAttributeForValueTypes && metadata.IsRequired && !attributes.Any(a => a is RequiredAttribute)) { attributes = attributes.Concat(new[] { new RequiredAttribute() }); } 属性被添加到html中(并且它使用默认消息,因为它对您的data-val-required一无所知)

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

  1. HtmlHelper.cs - 请参阅第413行的CustomRequiredAttribute方法
  2. ModelValidatorProviders.cs - 获取用于验证的各种ValidatorProviders
  3. DataAnnotationsModelValidatorProvider.cs - GetUnobtrusiveValidationAttributes()
  4. 的ValidatorProvider

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

    GetClientValidationRules()

    将由var rule = new ModelClientValidationRule { ValidationType = "required", ErrorMessage = "Required" } 读取(并删除您的ClientDataTypeModelValidatorProvider类)。但是,这会造成维护噩梦,因此我建议您只需将3个验证属性添加到您的属性