DataAnnotations динамически присоединяемые атрибуты - PullRequest
31 голосов
/ 31 августа 2010

Очевидно, что можно динамически прикреплять атрибуты DataAnnotation к свойствам объекта во время выполнения и таким образом достигать динамической проверки.

Может ли кто-нибудь предоставить пример кода по этому вопросу?

Ответы [ 4 ]

41 голосов
/ 31 августа 2010

MVC имеет крючок для предоставления вашего собственного ModelValidatorProvider. По умолчанию MVC 2 использует подкласс ModelValidatorProvider с именем DataAnnotationsModelValidatorProvider, который может использовать атрибуты 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; }
}
8 голосов
/ 19 июня 2013

В вашем global.asax вы должны очистить ModelValidatorProviders перед добавлением нового. В противном случае он будет добавлять каждую аннотацию два раза, что даст вам «Имена типов проверки в ненавязчивых правилах проверки клиента должны быть уникальными.» - ошибка.

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

    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);
}
0 голосов
/ 04 июля 2014

Подход с использованием пользовательского MetadataValidationProvider с переопределенным GetValidators имеет несколько недостатков:

  • Некоторые атрибуты, такие как DisplayAttribute, не имеют отношения к проверке, поэтому их добавление на этапе проверки не работает.
  • Это не может быть ориентировано на будущее; обновление инфраструктуры может привести к тому, что оно перестанет работать.

Если вы хотите, чтобы ваши динамически применяемые аннотации данных работали согласованно, вы можете создать подкласс DataAnnotationsModelMetadataProvider и DataAnnotationsModelValidatorProvider. После этого заменит фреймворка через ModelMetadataProviders.Current и ModelValidatorProviders.Providers при запуске приложения. (Вы можете сделать это в Application_Start.)

Когда вы создаете подкласс для встроенных провайдеров, систематический и, как мы надеемся, способ применения ваших собственных атрибутов в будущем будет переопределять GetTypeDescriptor. Я сделал это успешно, но это включало создание реализации ICustomTypeDescriptor и PropertyDescriptor, что требовало много кода и времени.

0 голосов
/ 31 августа 2010

Я не думаю, что вы можете добавлять атрибуты к членам во время выполнения, но вы, вероятно, могли бы использовать собственный поставщик метаданных, чтобы справиться с этим за вас.

Вы должны проверить это сообщение в блоге .

...