DataAnnotations динамически присоединяемые атрибуты
По-видимому, можно динамически прикреплять атрибуты DataAnnotation к свойствам объекта во время выполнения и, таким образом, достигать динамической проверки.
Может ли кто-нибудь предоставить пример кода по этому вопросу?
4 ответа
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);
}
Конечный результат:
с этой моделью:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
В вашем global.asax вы должны очистить ModelValidatorProviders перед добавлением нового. В противном случае он будет добавлять каждую аннотацию два раза, что даст вам "Имена типов проверки в ненавязчивых правилах проверки клиента должны быть уникальными."- ошибка.
protected void Application_Start()
{
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new CustomMetadataValidationProvider());
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
Подход с использованием пользовательских MetadataValidationProvider
с переопределением GetValidators
имеет несколько недостатков:
- Некоторые атрибуты, такие как
DisplayAttribute
не связаны с проверкой, поэтому добавление их на этапе проверки не работает. - Это не может быть ориентировано на будущее; обновление инфраструктуры может привести к тому, что оно перестанет работать.
Если вы хотите, чтобы ваши динамически применяемые аннотации данных работали согласованно, вы можете создать подкласс DataAnnotationsModelMetadataProvider
а также DataAnnotationsModelValidatorProvider
, После этого замените рамки через ModelMetadataProviders.Current
а также ModelValidatorProviders.Providers
при запуске приложения. (Вы могли бы сделать это в Application_Start
.)
Когда вы создаете подкласс для встроенных провайдеров, систематический и, как мы надеемся, способ применения ваших собственных атрибутов в будущем будет переопределять GetTypeDescriptor
, Я сделал это успешно, но это включало создание реализации ICustomTypeDescriptor
а также PropertyDescriptor
, что потребовало много кода и времени.
Я не думаю, что вы можете добавлять атрибуты к членам во время выполнения, но вы, вероятно, могли бы использовать собственный поставщик метаданных, чтобы справиться с этим для вас.
Вы должны проверить это сообщение в блоге.