asp.net-mvc DataAnnotations 动态附加属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3607247/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 00:26:38  来源:igfitidea点击:

DataAnnotations dynamically attaching attributes

asp.net-mvcdata-annotations

提问by mare

Apparently it is possible to dynamically attach DataAnnotation attributes to object properties at runtime and as such achieve dynamic validation.

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

Can someone provide code sample on this?

有人可以提供这方面的代码示例吗?

回答by John Farrell

MVC has a hook to provide your own ModelValidatorProvider. By default MVC 2 uses a sub class of ModelValidatorProvider called DataAnnotationsModelValidatorProvider that is able to use System.DataAnnotations.ComponentModel.ValidationAttribute attributes for validation.

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

The DataAnnotationsModelValidatorProvider uses reflection to find all the ValidationAttributes and simply loops through the collection to validate your models. All you need to do is override a method called GetValidators and inject your own attributes from whichever source you choose. I use this technique to do convention validations, the properties with DataType.Email attribute always gets passed through a regex, and use this technique to pull information from the database to apply more restrictive validations for "non-power" users.

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

The following example simply says "always make any FirstName properties required":

下面的例子只是说“总是需要任何 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);
    }
}

All you have to do is register the provider in your Global.asax.cs file:

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

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

        AreaRegistration.RegisterAllAreas();

        RegisterRoutes(RouteTable.Routes);
    }

The end result:

最终结果:

end result

最终结果

with this model:

使用此模型:

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

回答by Frank Horemans

In your global.asax you have to clear the ModelValidatorProviders before adding the new one. Otherwise it will add every annotation two times which will give you a "Validation type names in unobtrusive client validation rules must be unique."-error.

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

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

    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);
}

回答by Sam

The approach of using a custom MetadataValidationProviderwith an overridden GetValidatorshas a few weaknesses:

使用MetadataValidationProvider带有覆盖的自定义的方法GetValidators有一些弱点:

  • Some attributes such as DisplayAttributearen't related to validation, so adding them at the validation stage doesn't work.
  • It may not be future-proof; a framework update could cause it to stop working.
  • 某些属性(例如)DisplayAttribute与验证无关,因此在验证阶段添加它们不起作用。
  • 它可能不是面向未来的;框架更新可能会导致它停止工作。

If you want your dynamically-applied data annotations to work consistently, you can subclass DataAnnotationsModelMetadataProviderand DataAnnotationsModelValidatorProvider. After doing this, replacethe framework's ones via ModelMetadataProviders.Currentand ModelValidatorProviders.Providersat application start-up. (You could do it in Application_Start.)

如果您希望动态应用的数据注释始终如一地工作,您可以子类化DataAnnotationsModelMetadataProviderDataAnnotationsModelValidatorProvider。执行此操作后,通过和在应用程序启动时替换框架的。(你可以在.)ModelMetadataProviders.CurrentModelValidatorProviders.ProvidersApplication_Start

When you subclass the built-in providers, a systematic and hopefully future-proof way to apply your own attributes is to override GetTypeDescriptor. I've done this successfully, but it involved creating an implementation of ICustomTypeDescriptorand PropertyDescriptor, which required a lot of code and time.

当您对内置提供程序进行子类化时,应用您自己的属性的一种系统且希望面向未来的方法是覆盖GetTypeDescriptor. 我已经成功地做到了这一点,但它涉及创建ICustomTypeDescriptorand的实现PropertyDescriptor,这需要大量的代码和时间。

回答by Matthew Abbott

I don't think you can add attributes to members at runtime, but you could probably use a custom metadata provider to handle this for you.

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

You should check out this blog post.

你应该看看这篇博文