2011-12-02 9 views
14

que tienen un modelo simple:Cómo deshabilitar ModelMetadata.IsRequired de ser siempre cierto para el valor no anulable tipo

public class Sample 
{ 
    public bool A { get; set; } 

    [Required] 
    public bool B { get; set; } 
} 

Una obviamente, no es necesario. Por lo tanto, para la validación han establecido DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false en Global.asax.

también tengo un simple asistente de HTML que imprime verdadero o falso si se requiere el modelo:

public static class HtmlHelperExtensions 
{ 
    public static MvcHtmlString IsRequired<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression) 
    { 
     var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);   
     return new MvcHtmlString(metadata.IsRequired.ToString()); 
    } 
} 

También escribí una vista para mostrar mi problema:

@model MvcApplication10.Models.Sample 

A: @Html.IsRequired(m => m.A), B: @Html.IsRequired(m => m.B) 

lo que habría esperado esto para imprimir A: false, B: true, sin embargo, realmente imprime A: true, B: true.

¿Hay alguna manera de hacer que esta impresión sea el resultado esperado? IsRequired parece que siempre es verdadero aunque no configuré explícitamente el RequiredAttribute. El docs establece que es verdadero para los tipos de valores que no aceptan valores de null por defecto. ¿Cómo es que no hay una manera fácil de configurar esto en falso como lo hacemos con la validación?

EDITAR: Podría escribir un proveedor personalizado como este, pero me preguntaba si había una manera "fácil" en torno a esto:

public class ExtendedDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider 
{ 
    private static bool addImplicitRequiredAttributeForValueTypes = false; 

    public static bool AddImplicitRequiredAttributeForValueTypes 
    { 
     get 
     { 
      return addImplicitRequiredAttributeForValueTypes; 
     } 
     set 
     { 
      addImplicitRequiredAttributeForValueTypes = value; 
     } 
    } 

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 
    { 
     var result = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); 

     if (!AddImplicitRequiredAttributeForValueTypes && modelType.IsValueType && !attributes.OfType<RequiredAttribute>().Any()) 
     { 
      result.IsRequired = false; 
     } 

     return result; 
    } 
} 
+2

En MVC5 simplemente poniendo 'DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false' hace ahora tienen el efecto esperado de no añadir automáticamente' RequiredAttribute' de valor que no sea anulable tipos. – Cocowalla

Respuesta

3

Creo que se está frente a un insecto MVC. Se requiere siempre disparar en esa situación, no importa qué y aunque utilice

DataAnnotationsModelValidatorProvider 
    .AddImplicitRequiredAttributeForValueTypes = false; 

Esto fue discutido ya here e informó here. Este ejemplo va un paso más allá y muestra que cuando se activa un requerimiento implícito, no impide que se ejecute IValidatableObject. Si ejecuta la demostración desde el segundo enlace, puede reproducir su caso, donde sea necesario siempre es cierto.

De todos modos, esto es fácil de resolver porque si usted está diciendo que A is obviously not required es lo mismo que decir que es anulable, por lo que sólo hacerlo de esa manera:

public bool? A { get; set; } 
+0

Establecer la propiedad en 'bool?' Significa que no puedo usar helpers incorporados como 'CheckBoxFor' que requieren un booleano. Sí, podría escribir mi propio helper html, pero todavía tengo curiosidad de ver si escribir un modelo de proveedor de metadatos como lo hice en OP tiene más sentido. – TheCloudlessSky

+0

Siendo ese su comportamiento esperado, y considerando que MVC actualmente se comporta de esta manera, supongo que un proveedor personalizado es su única opción. – Joao

+0

¿La propiedad 'A' activa la validación? Debido a que 'IsRequired' es' true' no significa que la propiedad no sea válida. – Joao

0

he tenido este problema en varios coloca y ha implementado una solución simple como esta

if (metadata.ModelType == typeof(System.Boolean)) 
{ 
    metadata.IsRequired = false; 
} 
10

Como ha indicado, ValueTypes se establecerá en true de manera predeterminada. Para solucionar esto, puede buscar el RequiredAttribute si el tipo es un ValueType.

ModelMetadata metaData = ModelMetadata.FromLambdaExpression<TModel, TValue>(expression, html.ViewData); 

if ((metaData.ModelType.IsValueType && metaData.ModelType.GetCustomAttributes(typeof(RequiredAttribute), false).Any()) || 
    (!metaData.ModelType.IsValueType && metaData.IsRequired)) 
{ ... } 
+0

Esto es asombroso. En caso de que ayude a alguien, tuve que armar un método de extensión de etiqueta que coloque la cadena html "*" apropiada delante de la etiqueta de campo si el campo es obligatorio. Es MVC5, y todavía tenía algunos problemas con la devolución de los ValueType. Hay una gran cantidad de campos de formulario (49), por lo que modifiqué lo anterior para usar la reflexión para buscar los metadatos.ContainerType para ubicar la propiedad deseada: metaData.ModelType.IsValueType && metadata.ContainerType.GetProperty (metadata.PropertyName) .GetCustomAttributes (typeof (RequiredAttribute), false) .Any(). – bphillips

1

Lo siguiente arrojará verdadero o falso dependiendo de si tiene [Obligatorio] o no.

typeof(<YourModel>).GetProperty(<PropertyName>).GetCustomAttributes(typeof(RequiredAttribute), false).Any()

3

Si está trabajando en un EditorTemplate, como yo, que necesita un paso más:

var metaData = ModelMetadata.FromLambdaExpression(Model => Model, ViewData); 

var required = metaData.ContainerType.GetProperty(metaData.PropertyName).GetCustomAttributes(typeof (RequiredAttribute), false).Any(); 

Que necesita para obtener el tipo de contenedor desde el modelo de metadatos con el fin de verifica los atributos de tu propiedad específica; de lo contrario, solo está comprobando los atributos del tipo de datos de la propiedad, no los atributos de la propiedad.

+0

me salvaste el día: D – senzacionale

+0

Esto también funcionó en MVC5.2. @ ViewData.ModelMetadata.IsRequired –

0

Si quieres que funcione para los tipos anulables también:

private static bool RequiredAttrExists(ModelMetadata metaData) 
{ 
    if(!metaData.ModelType.IsValueType && metaData.IsRequired) 
     return true; 
    else if (metaData.ModelType.IsValueType && metaData.ContainerType.GetProperty(metaData.PropertyName).GetCustomAttributes(typeof(RequiredAttribute), false).Any()) 
     return true; 
    return false; 
} 
Cuestiones relacionadas