DataAnnotations动态附加属性

时间:2010-08-31 08:20:51

标签: asp.net-mvc data-annotations

显然,可以在运行时动态地将DataAnnotation属性附加到对象属性,从而实现动态验证。

有人可以提供代码示例吗?

4 个答案:

答案 0 :(得分:41)

MVC有一个钩子来提供你自己的ModelValidatorProvider。默认情况下,MVC 2使用名为DataAnnotationsModelValidatorProvider的ModelValidatorProvider子类,该子类能够使用System.DataAnnotations.ComponentModel.ValidationAttribute属性进行验证。

DataAnnotationsModelValidatorProvider使用反射来查找所有ValidationAttributes,并简单地遍历集合以验证模型。您需要做的就是覆盖一个名为GetValidators的方法,并从您选择的任何来源注入您自己的属性。我使用此技术进行常规验证,具有DataType.Email属性的属性始终通过正则表达式传递,并使用此技术从数据库中提取信息以对“非权力”用户应用更严格的验证。

以下示例简单地说“始终要求任何FirstName属性”:

 public class CustomMetadataValidationProvider : DataAnnotationsModelValidatorProvider
 {
    protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
    {
        //go to db if you want
        //var repository = ((MyBaseController) context.Controller).RepositorySomething;

        //find user if you need it
        var user = context.HttpContext.User;

        if (!string.IsNullOrWhiteSpace(metadata.PropertyName) && metadata.PropertyName == "FirstName")
            attributes = new List<Attribute>() {new RequiredAttribute()};

        return base.GetValidators(metadata, context, attributes);
    }
}

您所要做的就是在Global.asax.cs文件中注册提供程序:

    protected void Application_Start()
    {
        ModelValidatorProviders.Providers.Add(new CustomMetadataValidationProvider());

        AreaRegistration.RegisterAllAreas();

        RegisterRoutes(RouteTable.Routes);
    }

最终结果:

end result

使用此模型:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Birthday { get; set; }
}

答案 1 :(得分:8)

在global.asax中,您必须先清除ModelValidatorProviders,然后再添加新的。否则,它将添加两次每个注释,这将为您提供“不显眼的客户端验证规则中的验证类型名称必须是唯一的。” - 错误。

protected void Application_Start()
{
    ModelValidatorProviders.Providers.Clear();
    ModelValidatorProviders.Providers.Add(new CustomMetadataValidationProvider());

    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);
}

答案 2 :(得分:0)

将自定义MetadataValidationProvider与被覆盖的GetValidators一起使用的方法存在一些缺点:

  • 某些属性(例如DisplayAttribute)与验证无关,因此在验证阶段添加它们不起作用。
  • 可能不会出现面向未来;框架更新可能导致它停止工作。

如果您希望动态应用的数据注释能够一致地工作,则可以继承DataAnnotationsModelMetadataProviderDataAnnotationsModelValidatorProvider。完成此操作后,在应用程序启动时通过ModelMetadataProviders.CurrentModelValidatorProviders.Providers替换框架的框架。 (你可以在Application_Start中完成。)

当您对内置提供程序进行子类化时,应用您自己的属性的系统且希望面向未来的方法是覆盖GetTypeDescriptor。我已成功完成此操作,但它涉及创建ICustomTypeDescriptorPropertyDescriptor的实现,这需要大量代码和时间。

答案 3 :(得分:-1)

我认为您不能在运行时向成员添加属性,但您可以使用自定义元数据提供程序来为您处理此问题。

您应该查看this blog post