2011-02-28 8 views
50

Ho la seguente definizione ViewModelViewModel convalida per un elenco

public class AccessRequestViewModel 
{ 
    public Request Request { get; private set; } 
    public SelectList Buildings { get; private set; } 
    public List<Person> Persons { get; private set; } 
} 

Così nella mia richiesta ci deve essere almeno 1 persona per una richiesta di accesso. Quale approccio potresti usare per convalidare? Non voglio che questa convalida avvenga nel mio controller, cosa che sarebbe semplice da fare. L'unica scelta è un attributo di convalida personalizzato?

Edit: Attualmente l'esecuzione di questa convalida con FluentValidation (bella libreria!)

RuleFor(vm => vm.Persons) 
       .Must((vm, person) => person.Count > 0) 
       .WithMessage("At least one person is required"); 

risposta

122

Se si utilizza annotazioni di dati per eseguire la convalida potrebbe essere necessario un attributo personalizzato:

public class EnsureOneElementAttribute : ValidationAttribute 
{ 
    public override bool IsValid(object value) 
    { 
     var list = value as IList; 
     if (list != null) 
     { 
      return list.Count > 0; 
     } 
     return false; 
    } 
} 

e poi:

[EnsureOneElement(ErrorMessage = "At least a person is required")] 
public List<Person> Persons { get; private set; } 

o per renderlo più generico:

public class EnsureMinimumElementsAttribute : ValidationAttribute 
{ 
    private readonly int _minElements; 
    public EnsureMinimumElementsAttribute(int minElements) 
    { 
     _minElements = minElements; 
    } 

    public override bool IsValid(object value) 
    { 
     var list = value as IList; 
     if (list != null) 
     { 
      return list.Count >= _minElements; 
     } 
     return false; 
    } 
} 

e poi:

[EnsureMinimumElements(1, ErrorMessage = "At least a person is required")] 
public List<Person> Persons { get; private set; } 

Personalmente io uso FluentValidation.NET invece di annotazioni di dati per eseguire la convalida perché preferisco la logica imperativa convalida al posto del dichiarativa. Penso che sia più potente. Quindi la mia regola di convalida sarebbe semplicemente simile a questa:

RuleFor(x => x.Persons) 
    .Must(x => x.Count > 0) 
    .WithMessage("At least a person is required"); 
+0

Sembra che ho bisogno usare il sovraccarico Must() per utilizzare person.Count, vedere la mia modifica e fammi sapere se hai una versione più amichevole :) – ryan

+1

@ryan, infatti ci sono due overload di questo metodo come [mostrato nella documentazione] (http: // fluentvalidation. codeplex.com/wikipage?title=Validators&referringTitle=Documentation&ANCHOR#Predicate). Quindi la mia versione è più amichevole. Non preoccuparti se Visual Studio lo sottolinea come errore. Dovrebbe funzionare se si tenta di compilare. È solo che VS Intellisense non è abbastanza avanzato per capirlo :-) Quindi 'RuleFor (x => x.Persons) .Must (x => x.Count> 0) .WithMessage (" Almeno una persona è richiesta ") ; 'compilerà e funzionerà correttamente. –

+0

Strano, ora non sta sottolineando. Grazie! – ryan

0

Un approccio potrebbe essere quello di utilizzare un costruttore privato e un metodo statico per restituire un'istanza dell'oggetto.

public class AccessRequestViewModel 
{ 
    private AccessRequesetViewModel() { }; 

    public static GetAccessRequestViewModel (List<Person> persons) 
    { 
      return new AccessRequestViewModel() 
      { 
       Persons = persons, 
      }; 
    } 

    public Request Request { get; private set; } 
    public SelectList Buildings { get; private set; } 
    public List<Person> Persons { get; private set; } 
} 

Utilizzando sempre la fabbrica per creare un'istanza di ViewModel, è possibile assicurarsi che ci sia sempre una persona.

Questo probabilmente non è l'ideale per quello che vuoi, ma probabilmente funzionerebbe.

0

Sarebbe molto pulito ed elegante avere una convalida personalizzata. Qualcosa del genere:

public class AccessRequestViewModel 
{ 
    public Request Request { get; private set; } 
    public SelectList Buildings { get; private set; } 
    [AtLeastOneItem] 
    public List<Person> Persons { get; private set; } 
} 

oppure [MinimumItems(1)].

+0

Vedere [questa risposta] (http://stackoverflow.com/a/5146766/163507). – Rolf

1

Avete due scelte qui, o creare un attributo di convalida personalizzato e decorare la proprietà con esso, oppure si può fare il vostro ViewModel implementi l'interfaccia IValidatableObject (che definisce un metodo Validate)

Spero che questo aiuti :)

7

Un altro modo possibile per gestire le convalide del conteggio per i membri della raccolta dell'oggetto del modello di visualizzazione consiste nell'avere una proprietà calcolata che restituisce il conteggio della raccolta o dell'elenco. Un RangeAttribute può quindi essere applicato come nel seguente codice per applicare validazione conteggio:

[Range(minimum: 1, maximum: Int32.MaxValue, ErrorMessage = "At least one item needs to be selected")] 
public int ItemCount 
{ 
    get 
    { 
     return Items != null ? Items.Length : 0; 
    } 
} 

Nel codice precedente, ItemCount è un esempio calcolata alloggio su una modello vista essendo convalidato, e gli articoli è una proprietà della raccolta membro esempio cui il conteggio viene controllato. In questo esempio, almeno un elemento viene applicato al membro della raccolta e il limite massimo è il valore massimo che può assumere un numero intero, che è, per la maggior parte degli scopi pratici, illimitato. Il messaggio di errore sull'errore di convalida può anche essere impostato tramite il membro ErrorMessage di RangeAttribute nell'esempio sopra riportato.

1

Il seguente codice funziona in asp.net core 1.1.

[Required, MinLength(1, ErrorMessage = "At least one item required in work order")] 
public ICollection<WorkOrderItem> Items { get; set; } 
0

La risposta di Darin è buona, ma la versione seguente ti fornirà automaticamente un utile messaggio di errore.

public class MinimumElementsAttribute : ValidationAttribute 
{ 
    private readonly int minElements; 

    public MinimumElementsAttribute(int minElements) 
    { 
     this.minElements = minElements; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var list = value as IList; 

     var result = list?.Count >= minElements; 

     return result 
      ? ValidationResult.Success 
      : new ValidationResult($"{validationContext.DisplayName} requires at least {minElements} element" + (minElements > 1 ? "s" : string.Empty)); 
    } 
} 

Usage:

[MinimumElements(1)] 
    public List<Customer> Customers {get;set} 

    [MinimumElements(2)] 
    public List<Address> Addresses {get;set} 

Messaggio di errore:

  • clienti richiede almeno 1 elemento
  • Indirizzi richiede almeno 2 elementi
Problemi correlati