2011-08-25 9 views
5

Ho una vista fortemente digitata che contiene un attributo DropDownListFor.Convalida guida

Ogni elemento nell'elenco a discesa è rappresentato da un GUID.

Quello che sto cercando è un modo per convalidare se un utente seleziona un elemento dall'elenco a discesa. Al momento non vedo comunque di farlo usando le Annotazioni dei dati.

È comunque possibile ottenere ciò utilizzando le annotazioni di dati, in modo che la convalida lato client e server funzioni.

Sto indovinando ho bisogno di fare un metodo personalizzato per farlo, ma mi chiedevo se qualcosa già esisteva.

risposta

8

risposta a cura

Rileggendo la tua domanda, suona come si vuole solo sapere se si seleziona un valore. Se questo è il caso, allora basta applicare il RequiredAttribute alla proprietà Guid e rendere annullabile sul modello

public class GuidModel 
{ 
    [Required] 
    public Guid? Guid { get; set; } 

    public IEnumerable<Guid> Guids { get; set; } 
} 

poi nel fortemente tipizzato View (con @model GuidModel)

@Html.ValidationMessageFor(m => m.Guid) 
@Html.DropDownListFor(
    m => m.Guid, 
    Model.Guids.Select(g => new SelectListItem {Text = g.ToString(), Value = g.ToString()}), 
    "-- Select Guid --") 

Aggiungere lo script JavaScript convalida del client riferimenti per la validazione lato client.

Il controller sembra

public class GuidsController : Controller 
{ 
    public GuidRepository GuidRepo { get; private set; } 

    public GuidsController(GuidRepository guidRepo) 
    { 
     GuidRepo = guidRepo; 
    } 

    [HttpGet] 
    public ActionResult Edit(int id) 
    { 
     var guid = GuidRepo.GetForId(id); 
     var guids - GuidRepo.All(); 

     return View(new GuidModel { Guid = guid, Guids = guids }); 
    } 

    [HttpPost] 
    public ActionResult Edit(GuidModel model) 
    { 
     if (!ModelState.IsValid) 
     { 
      model.Guids = GuidRepo.All(); 
      return View(model); 
     } 

     /* update db */ 

     return RedirectToAction("Edit"); 
    } 
} 

Questo farà sì che la proprietà Guid è richiesto per un modello-bound GuidModel.

risposta originale

Non credo che ci sia un attributo ready made dati di annotazione di convalida che è in grado di fare questo. Ho scritto a blog post about one way to achieve this; il post sta usando un contenitore IoC ma potresti prendere la dipendenza hard coded se vuoi far funzionare qualcosa.

Qualcosa di simile

public class ValidGuidAttribute : ValidationAttribute 
{ 
    private const string DefaultErrorMessage = "'{0}' does not contain a valid guid"; 

    public ValidGuidAttribute() : base(DefaultErrorMessage) 
    { 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var input = Convert.ToString(value, CultureInfo.CurrentCulture); 

     // let the Required attribute take care of this validation 
     if (string.IsNullOrWhiteSpace(input)) 
     { 
      return null; 
     } 

     // get all of your guids (assume a repo is being used) 
     var guids = new GuidRepository().AllGuids(); 

     Guid guid; 
     if (!Guid.TryParse(input, out guid)) 
     { 
      // not a validstring representation of a guid 
      return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); 
     } 

     // is the passed guid one we know about? 
     return guids.Any(g => g == guid) ? 
      new ValidationResult(FormatErrorMessage(validationContext.DisplayName)) : null; 
    } 
} 

e poi sul modello che si invia in azione di controllo

public class GuidModel 
{ 
    [ValidGuid] 
    public Guid guid { get; set; } 
} 

Questo ti dà la validazione lato server. Puoi scrivere anche la convalida lato client per fare questo, magari usando RemoteAttribute ma in questo caso non vedo molto valore in quanto le uniche persone che vedranno questa convalida lato client sono le persone che fanno scherzi ai valori nel DOM; non sarebbe di nessun beneficio per il tuo normale utente.

+1

Che in realtà non funzionerà se non si contrassegna la proprietà Guid come annullabile, vedere la mia risposta. –

+0

@NickAlbrecht grazie, corretto –

15

In realtà, non è possibile utilizzare Required attributo con GUID (senza il metodo Cito qui di seguito) perché ereditano da struct, e come tali il loro valore di default è in realtà un esempio di Guid.Empty, in grado di soddisfare le esigenze di l'attributo Required. Detto questo, è possibile ottenere ciò che si desidera, basta rendere la proprietà nullable, prendi questo per esempio ...

public class Person 
{ 
    [Required] //Only works because the Guid is nullable 
    public Guid? PersonId { get; set;} 
    public string FirstName { get; set;} 
    public string LastName { get; set;} 
} 

Segnando l'annullabile GUID (utilizzando il?, O Null, se si preferisce la strada più lunga) si lascia stare come nullo durante l'associazione contro ciò che il browser ha inviato. Nel tuo caso, assicurati che il valore dell'opzione predefinita del menu a discesa utilizzi una stringa vuota come valore.

EDIT: L'unica avvertenza di questo metodo è che si finisce per dover usare qualcosa come Person.GetValueOfDefault() ovunque e potenzialmente test per Guid.Empty. Mi sono stancato di fare questo e ho finito per creare il mio attributo di convalida per semplificare la validazione di Guids (e di qualsiasi altro tipo che ha valori predefiniti che voglio considerare non validi come int, DateTime, ecc.). Tuttavia non ho ancora la convalida sul lato client per andare avanti con questo, quindi la convalida avviene solo sul server. Tuttavia questo può essere combinato con [Required] (progettato per non duplicare la funzionalità di [Required]) se si sta usando i tipi nullable. Poi almeno riceverai la validazione lato client per [Required]. Ciò significa che devi ancora usare GetValueOrDefault(), ma almeno non devi più provare per Guid.Empty. Il collegamento Gist ha alcuni XMLDocs con esempi, li ho lasciati qui per brevità. Attualmente sto usando con ASP.NET Core.

Gist: RequireNonDefaultAttribute

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] 
public class RequireNonDefaultAttribute : ValidationAttribute 
{ 
    public RequireNonDefaultAttribute() 
     : base("The {0} field requires a non-default value.") 
    { 
    } 

    public override bool IsValid(object value) 
    { 
     return value != null && !Equals(value, Activator.CreateInstance(value.GetType())); 
    } 
} 
+0

ho provato ad usare il tuo codice con proprietà come sotto in un modello e il modello fallisce se fornisco un valore nullo? pensavo che null fosse ok ma non 0 in questo caso secondo l'esempio nel sommario. Si prega di confermare [RequiredNonDefault] int pubblico? Prova {ottieni; impostato; } – Krishna

+0

Questo perché il tuo tipo è 'int ?. E il valore predefinito di qualsiasi oggetto 'Nullable <>', è null, quindi null non è valido ma 0 lo è. Comunque sei corretto nel senso che il mio esempio implica il contrario. Lo aggiusterò. Per ottenere quello che stai descrivendo per funzionare (permettendo null, ma non 0), è meglio usare qualcosa di simile a '[Range (1, int.MaxValue)] int? i {get; set;} ' –

+0

@Albercht Grazie per la conferma. – Krishna

3

So che questa è una vecchia questione, ma se qualcun altro è interessato sono riuscito ad ottenere intorno a questo con la creazione di un'annotazione [IsNotEmpty] (facendo il nullable Guid non era un'opzione nel mio caso).

Questo utilizza la riflessione per capire se esiste un'implementazione di Empty sulla proprietà e, in caso affermativo, la confronta.

public class IsNotEmptyAttribute : ValidationAttribute 
{ 

    public override bool IsValid(object value) 
    { 

     if (value == null) return false; 

     var valueType = value.GetType(); 
     var emptyField = valueType.GetField("Empty"); 

     if (emptyField == null) return true; 

     var emptyValue = emptyField.GetValue(null); 

     return !value.Equals(emptyValue); 

    } 
}