2015-06-17 10 views
5

Ci sono alcune proprietà nel mio modello di vista che sono facoltative al momento del salvataggio, ma necessarie quando si invia. In una parola, permettiamo il salvataggio parziale, ma l'intero modulo viene inviato, vogliamo essere sicuri che tutti i campi richiesti abbiano dei valori.Come convalidare in modo selettivo un attributo di annotazione dati?

Gli unici approcci posso pensare in questo momento sono:

manipolare la raccolta errori ModelState.

Il modello vista ha tutti gli attributi [Required] in atto. Se la richiesta è parziale, lo diventa false quando si immette l'azione del controller. Quindi eseguo tutti gli errori ModelState (che è un ICollection<KeyValuePair<string, ModelState>>) e rimuovi tutti gli errori generati dalle proprietà [Required].

Ma se la richiesta è di inviare l'intero modulo, non interferirò con gli attributi ModelState e con gli attributi [Required].

utilizzare diversi modelli di visualizzazione per parziale salvare e presentare

Questo è ancora più brutto. Un modello di vista conterrà tutti gli attributi [Required], utilizzati da un metodo di azione per l'invio. Ma per il salvataggio parziale, inserisco i dati del modulo in un'azione diversa che utilizza uno stesso modello di visualizzazione senza tutti gli attributi [Required].

Ovviamente, vorrei finire con un sacco di modelli duplicati codice/vista.

La soluzione ideale

ho pensato se riesco a creare un attributo di annotazione di dati personalizzati [SubmitRequired] per quelle proprietà richieste. In qualche modo, la convalida la ignora quando si salva parzialmente ma non quando si invia.

ancora non poteva avere un indizio chiaro. Chiunque può aiutare? Grazie.

+3

È possibile utilizzare un [infallibile] (http://foolproof.codeplex.com/) '[RequiredIfTrue]' o un attributo simile (basato su un'ulteriore proprietà 'boRa SubmitRequired' nel proprio modello di vista. è 'false' quindi nessuna validazione verrà eseguita sulle proprietà decorate con' [RequiredIfTrue "SubmitRequired"] ', altrimenti le proprietà verranno convalidate –

+0

Grazie, Stephen. Quel pacchetto è una beta con un ultimo aggiornamento nel 2012. Ma questo 'RequiredIf' ha puntato nella giusta direzione Apprezziamo molto – Blaise

+0

Consentitemi di allegare il collegamento al codice che userò: http://foolproof.codeplex.com/SourceControl/latest#Foolproof/RequiredIf.cs – Blaise

risposta

0

Il mio approccio è quello di aggiungere l'attributo condizionale verifica annotazione, che viene appreso da foolproof.

Fare parte SaveMode del modello di vista.

Contrassegnare le proprietà nullable in modo che i cui valori siano facoltativi quando SaveMode non è Finalize.

Ma aggiungere un attributo di annotazione personalizzate [FinalizeRequired]:

[FinalizeRequired] 
public int? SomeProperty { get; set; } 

[FinalizeRequiredCollection] 
public List<Item> Items { get; set; } 

ecco il codice per l'attributo:

[AttributeUsage(AttributeTargets.Property)] 
public abstract class FinalizeValidationAttribute : ValidationAttribute 
{ 
    public const string DependentProperty = "SaveMode"; 

    protected abstract bool IsNotNull(object value); 

    protected static SaveModeEnum GetSaveMode(ValidationContext validationContext) 
    { 
     var saveModeProperty = validationContext.ObjectType.GetProperty(DependentProperty); 

     if (saveModeProperty == null) return SaveModeEnum.Save; 

     return (SaveModeEnum) saveModeProperty.GetValue(validationContext.ObjectInstance); 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var saveMode = GetSaveMode(validationContext); 

     if (saveMode != SaveModeEnum.SaveFinalize) return ValidationResult.Success; 

     return (IsNotNull(value)) 
      ? ValidationResult.Success 
      : new ValidationResult(string.Format("{0} is required when finalizing", validationContext.DisplayName)); 
    } 
} 

Per i tipi di dati primitivi, controllare value!=null:

[AttributeUsage(AttributeTargets.Property)] 
public class FinalizeRequiredAttribute : FinalizeValidationAttribute 
{ 
    protected override bool IsNotNull(object value) 
    { 
     return value != null; 
    } 
} 

Per Collezioni IEnumerable,

[AttributeUsage(AttributeTargets.Property)] 
public class FinalizeRequiredCollectionAttribute : FinalizeValidationAttribute 
{ 
    protected override bool IsNotNull(object value) 
    { 
     var enumerable = value as IEnumerable; 
     return (enumerable != null && enumerable.GetEnumerator().MoveNext()); 
    } 
} 

Questo approccio consente di ottenere la separazione dei problemi eliminando la logica di convalida dal controller. Gli attributi di annotazione dei dati devono gestire questo tipo di lavoro, il cui controller richiede solo un controllo di !ModelState.IsValid. Ciò è particolarmente utile nella mia applicazione, perché non sarei in grado di effettuare il refactoring su un controller di base se il controllo ModelState è diverso in ogni controller.

1

penso che ci sia una soluzione più precisa per il vostro problema. Diciamo che stai inviando un metodo, voglio dire che stai chiamando lo stesso metodo per l'invio parziale e completo. Quindi procedere come segue:

 [HttpPost] 
     [ValidateAntiForgeryToken] 
     public ActionResult YourMethod(ModelName model) 
     { 
      if(partialSave) // Check here whether it's a partial or full submit 
      { 
      ModelState.Remove("PropertyName"); 
      ModelState.Remove("PropertyName2"); 
      ModelState.Remove("PropertyName3"); 
      } 

      if (ModelState.IsValid) 
      { 
      } 
     } 

Questo dovrebbe risolvere il problema. Fammi sapere se hai problemi.

Edit:

Come @SBirthare ha commentato che la sua non è fattibile per aggiungere o rimuovere proprietà quando il modello si aggiorna, ho trovato sotto soluzione che dovrebbe funzionare per l'attributo [Required].

ModelState.Where(x => x.Value.Errors.Count > 0).Select(d => d.Key).ToList().ForEach(g => ModelState.Remove(g)); 

Sopra il codice otterrà tutte le chiavi che avrebbero un errore e rimuoverle dallo stato del modello. È necessario inserire questa linea in se condizione per assicurarsi che venga eseguito in invio parziale. Ho anche controllato che l'errore verrebbe fornito solo per l'attributo [Required] (in qualche modo il raccoglitore di modelli dà alta priorità a questo attributo, anche se lo posizioni dopo/sotto qualsiasi altro attributo). Quindi non devi più preoccuparti degli aggiornamenti del modello.

+0

Questa è in effetti la prima soluzione che ho proposto: la modifica di 'ModelState' nell'azione del controller. Ma grazie per l'input. – Blaise

+0

La tua soluzione proposta non è economica cara. Hai proposto di scorrere tutte le proprietà. Mentre sto proponendo di rimuovere quelli che sono richiesti ma non al momento del post parziale. –

+1

Anche se questa potrebbe essere una soluzione semplice e funziona bene nei casi in cui si hanno poche proprietà, non è elegante. Senza offesa. Si interrompe solo l'OCP per uno, cioè una nuova proprietà aggiunta al modello, devi venire qui e aggiungere alla lista. – SBirthare

2

Questo è uno approccio che uso nei progetti.

Creare un ValidationService<T> contenente la business logic che verificherà che il modello sia in uno stato valido da inviare con un metodo IsValidForSubmission.

Aggiungere una proprietà al modello di vista che si controlla prima di chiamare il metodo IsValidForSubmission.

Solo utilizzare il costruito nel convalida gli attributi per il controllo per i dati non validi vale a dire lunghezze di campo ecc

creare alcuni attributi personalizzati all'interno di uno spazio dei nomi diverso che avrebbe convalidare in alcuni scenari vale a dire [RequiredIfSubmitting] e quindi utilizzare riflessione all'interno del vostro servizio per iterare sugli attributi di ciascuna proprietà e chiama il loro metodo IsValid manualmente (ignorando quelli che non si trovano nel tuo spazio dei nomi).

Ciò popolano e restituire un Dictionary<string, string> che può essere utilizzato per popolare ModelState tornare all'interfaccia utente:

var validationErrors = _validationService.IsValidForSubmission(model); 

if (validationErrors.Count > 0) 
{ 
    foreach (var error in validationErrors) 
    { 
     ModelState.AddModelError(error.Key, error.Value); 
    } 
} 
Problemi correlati