Apparently it is possible to dynamically attach DataAnnotation attributes to object properties at runtime and as such achieve dynamic validation.
Can someone provide code sample on this?
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.
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.
The following example simply says "always make any FirstName properties required":
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:
protected void Application_Start()
{
ModelValidatorProviders.Providers.Add(new CustomMetadataValidationProvider());
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
The end result:
with this model:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
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.
protected void Application_Start()
{
ModelValidatorProviders.Providers.Clear();
ModelValidatorProviders.Providers.Add(new CustomMetadataValidationProvider());
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With