2010-08-31 19 views

Respuesta

39

MVC tiene un gancho para proporcionar su propio ModelValidatorProvider. De forma predeterminada, MVC 2 usa una subclase de ModelValidatorProvider llamada DataAnnotationsModelValidatorProvider que puede usar los atributos System.DataAnnotations.ComponentModel.ValidationAttribute para la validación.

El DataAnnotationsModelValidatorProvider utiliza la reflexión para encontrar todos los ValidationAttributes y simplemente recorre la colección para validar sus modelos. Todo lo que necesita hacer es anular un método llamado GetValidators e inyectar sus propios atributos de la fuente que elija. Utilizo esta técnica para hacer validaciones de convenciones, las propiedades con el atributo DataType.Email siempre se pasan a través de una expresión regular, y utilizo esta técnica para extraer información de la base de datos para aplicar validaciones más restrictivas para usuarios "no poderosos".

el siguiente ejemplo se dice simplemente "siempre que las propiedades requeridas 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); 
    } 
} 

Todo lo que tiene que hacer es registrar el proveedor en su archivo Global.asax.cs:

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

     AreaRegistration.RegisterAllAreas(); 

     RegisterRoutes(RouteTable.Routes); 
    } 

El resultado final:

end result

w ITH este modelo:

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

esto es genial jfar! ;) Creo que me salvaste una inmersión profunda en VAB .. – mare

+0

Un problema con el código de ejemplo en esta respuesta es que los atributos se pueden procesar dos veces: una vez por tu subclase de 'DataAnnotationsModelValidatorProvider' y una por la preexistente. Esto puede conducir a errores. Creo que es mejor pasar atributos que está agregando a la llamada a 'base.GetValidators' para evitar esto. – Sam

+1

Además, en ASP.NET MVC 4 (y posiblemente otros), 'base.GetValidators' automáticamente agrega' RequiredAttribute' cuando 'metadata.IsRequired' es' true'. Una forma de eludir esto es establecer 'metadata.IsRequired' en' false' antes de llamar a 'base.GetValidators'. Puede volver a establecer el valor original después de la llamada. – Sam

1

No creo que pueda agregar atributos a los miembros en el tiempo de ejecución, pero probablemente pueda usar un proveedor de metadatos personalizado para manejar esto por usted.

Debe consultar this blog post.

+4

¿Qué ofrece? Voto negativo y ningún comentario? –

+1

No fui yo el que votó negativamente, pero yo diría que esto no pasa la prueba de búsqueda de respuestas solo por enlace que fue el enlace para morir (como es actualmente el caso por cierto). Esta es una señal, tal vez una buena, pero los usuarios aún tendrán que irse del sitio para implementarla. Considere proporcionar más detalles en línea o convertir a un comentario, o aceptar el voto errante aquí y allá. – KyleMit

+2

Lo que por supuesto es EXACTAMENTE lo que sucedió, ya que ese enlace está muerto ahora. – StevoInco

8

En su global.asax se deben eliminar los ModelValidatorProviders antes de añadir el nuevo. De lo contrario, agregará cada anotación dos veces lo que le dará un "Los nombres de tipo de validación en las reglas de validación discretas del cliente deben ser únicas." - error.

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

    AreaRegistration.RegisterAllAreas(); 

    RegisterRoutes(RouteTable.Routes); 
} 
0

El enfoque de utilizar una costumbre MetadataValidationProvider con un sobrescrito GetValidators tiene algunos puntos débiles:

  • Algunos atributos tales como DisplayAttribute no están relacionados con la validación, por lo que la adición de ellos en la etapa de validación doesn' t trabajo.
  • Puede que no sea a prueba de futuro; una actualización del marco podría hacer que deje de funcionar.

Si desea que sus anotaciones de datos aplicados de forma dinámica para trabajar constantemente, usted puede subclase DataAnnotationsModelMetadataProvider y DataAnnotationsModelValidatorProvider. Después de hacer esto, reemplace los marcos a través de ModelMetadataProviders.Current y ModelValidatorProviders.Providers al inicio de la aplicación. (Puede hacerlo en Application_Start.)

Cuando subclasifica los proveedores incorporados, una forma sistemática y, con suerte, a prueba de futuro de aplicar sus propios atributos es anular GetTypeDescriptor. Lo hice con éxito, pero implicó la creación de una implementación de ICustomTypeDescriptor y PropertyDescriptor, que requería mucho código y tiempo.

Cuestiones relacionadas