2011-12-02 6 views
14

Ho un modello semplice:Come disattivare ModelMetadata.IsRequired da essere sempre vero per non annullabile tipo di valore

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

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

A non è, ovviamente, necessario. Pertanto, per la convalida è stato impostato DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false in Global.asax.

Ho anche un semplice aiuto html che consente di stampare vero o falso, se è richiesto il modello:

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()); 
    } 
} 

Ho anche scritto al fine di mostrare il mio problema:

@model MvcApplication10.Models.Sample 

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

mi sarei aspettato questo per stampare A: false, B: true, tuttavia, stampa effettivamente A: true, B: true.

C'è un modo per rendere questa stampa il mio risultato previsto? IsRequired sembra sempre restituire true anche se non ho impostato esplicitamente lo RequiredAttribute. Lo docs dichiara che è vero per i tipi di valore non annullabile per impostazione predefinita. Come mai non c'è un modo semplice per impostare questo a falso come possiamo con la convalida?

EDIT: Potrei scrivere un provider personalizzato come questo, ma mi chiedevo se ci fosse un modo "facile" intorno a questo:

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

In MVC5 impostando semplicemente 'DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false' fa ora hanno l'effetto atteso di non aggiungere automaticamente' RequiredAttribute' per il valore non annullabile tipi. – Cocowalla

risposta

3

Credo che sei di fronte un bug MVC. Richiesto sempre scattare su quella situazione, non importa cosa e anche se si utilizza

DataAnnotationsModelValidatorProvider 
    .AddImplicitRequiredAttributeForValueTypes = false; 

Questo è stato già discusso here e segnalato here. Questo esempio fa un passo avanti e mostra che quando un trigger implicito richiesto non impedisce l'esecuzione di IValidatableObject. Se si esegue la demo dal secondo link è possibile riprodurre il caso, dove richiesto è sempre vero.

In ogni caso, questo è facile da risolvere, perché se stai dicendo che A is obviously not required è lo stesso dicendo che è annullabile, quindi basta fare in questo modo:

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

Impostare la proprietà su 'bool? 'Significa che non posso usare helper html incorporati come' CheckBoxFor' che richiedono un booleano. Sì, potrei scrivere il mio helper html, ma sono comunque curioso di vedere se scrivere un provider di metadati modello come quello che ho fatto nell'OP abbia più senso. – TheCloudlessSky

+0

Essendo questo il tuo comportamento previsto e considerando che MVC si comporta attualmente in questo modo, suppongo che un fornitore personalizzato sia la tua unica opzione. – Joao

+0

È la convalida di attivazione della proprietà 'A'? Poiché 'IsRequired' è' true' non significa che la proprietà non sia valida. – Joao

0

Ho avuto questo problema in diversi luoghi e hanno implementato una semplice correzione come questa

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

Come è stato notato, ValueTypes verrà impostato su true. Per ovviare a questo, è possibile verificare il RequiredAttribute se il tipo è 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

Questo è fantastico. Nel caso in cui aiuti qualcuno, ho dovuto mettere insieme un metodo di estensione dell'etichetta che posiziona la stringa html appropriata "*" davanti all'etichetta del campo se il campo è richiesto. È MVC5 e stavo ancora riscontrando alcuni problemi con ValueTypes che restituisce true. Ci sono molti campi modulo (49), quindi ho modificato il precedente per usare reflection per cercare il metadata.ContainerType per localizzare la proprietà desiderata: metaData.ModelType.IsValueType && metadata.ContainerType.GetProperty (metadata.PropertyName) .GetCustomAttributes (typeof (RequiredAttribute), false) .Any(). – bphillips

1

Quanto segue restituirà vero o falso a seconda che si disponga o meno di [Richiesto].

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

3

Se si lavora in un EditorTemplate, come mi è stato, avrete bisogno di più ad un passo:

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

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

È necessario per ottenere il tipo di contenitore dal modello di metadati, al fine di controlla gli attributi della tua proprietà specifica; in caso contrario, è sufficiente controllare gli attributi del tipo di dati della proprietà, non gli attributi della proprietà.

+0

si salva la mia giornata: D – senzacionale

+0

Questo ha funzionato anche su MVC5.2. @ ViewData.ModelMetadata.IsRequired –

0

Se si vuole farlo funzionare per i tipi nullable troppo:

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; 
} 
Problemi correlati