2011-08-25 23 views
5

Tengo una vista fuertemente tipada que tiene un atributo DropDownListFor en ella.Validación de Guid

Cada elemento en la lista desplegable está representado por un GUID.

Lo que estoy buscando es una forma de validar si un usuario selecciona un elemento de la lista desplegable. En este momento no veo de todos modos hacer esto usando anotaciones de datos.

¿Hay alguna forma de lograr esto usando anotaciones de datos para que la validación del lado del cliente y del servidor funcione?

Supongo que tengo que hacer un método personalizado para hacer esto, pero me preguntaba si algo ya existía.

Respuesta

8

respuesta Editado

Al volver a leer su pregunta, parece que lo que desea saber si se ha seleccionado un valor. Si ese es el caso, entonces simplemente aplicar el RequiredAttribute a la propiedad Guid y que sea anulable en el modelo

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

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

continuación, en la vista fuertemente tipado (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 --") 

Añadir la secuencia de comandos JavaScript validación del cliente referencias para la validación del lado del cliente.

El controlador se parece

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

Esto asegurará que no se requiere la propiedad Guid para un modelo enlazado GuidModel.

respuesta original

no creo que hay un hecho atributo de anotación de datos de validación listo que es capaz de hacer esto. Escribí a blog post about one way to achieve this; la publicación utiliza un contenedor IoC, pero puede tomar la dependencia codificada si quiere que algo funcione.

Algo así como

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

y luego en el modelo que usted envía a la acción del controlador

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

Esto le da la validación del lado del servidor. También puede escribir la validación del lado del cliente para hacer esto, tal vez usando RemoteAttribute, pero no veo mucho valor en este caso, ya que las únicas personas que verán esta validación del lado del cliente son personas que están jugando con valores en el DOM; no sería beneficioso para su usuario normal.

+1

Eso realmente no funcionará a menos que marque la propiedad Guid como nulable, vea mi respuesta. –

+0

@NickAlbrecht gracias, corrigió –

15

En realidad, no se puede utilizar Required atributo con GUID (sin el método que menciono a continuación), ya que heredan de struct, y como tal, su valor por defecto es en realidad una instancia de Guid.Empty, que satisfaga los requisitos de el atributo Required. Ahora que se dice, es posible obtener lo que quiere, solo necesita hacer que su propiedad sea nulable, tome esto por ejemplo ...

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

Marcando el anulable GUID (utilizando el? O anulable si lo prefiere el camino más largo) que se deja permanecer como nulo cuando se unen contra lo que el navegador ha enviado. En su caso, solo asegúrese de que el valor de la opción predeterminada del menú desplegable use una cadena vacía como valor.

EDIT: La única salvedad a este método es que al final tener que usar algo como Person.GetValueOfDefault() todas partes y, potencialmente, las pruebas de Guid.Empty. Me cansé de hacer esto y terminé creando mi propio atributo de validación para ayudar a simplificar la validación de Guids (y cualquier otro tipo que tenga valores predeterminados que quiera tratar como no válidos, como int, DateTime, etc.). Sin embargo, no tengo la validación del lado del cliente para seguir adelante con esto, por lo que la validación solo ocurre en el servidor. Sin embargo, esto se puede combinar con [Required] (diseñado para no duplicar la funcionalidad de [Required]) si está de acuerdo con el uso de tipos que aceptan valores numéricos. Entonces al menos obtendrá la validación del lado del cliente para [Required]. Esto significa que todavía tiene que usar GetValueOrDefault(), pero al menos ya no tiene que probar Guid.Empty. El enlace Gist tiene algunos XMLDocs con ejemplos, los dejé aquí para abreviarlos. Actualmente lo estoy usando con ASP.NET Core.

Síntesis: 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

intenté usar su código con la propiedad siguiente en un modelo y el modelo falla si proporciono valor nulo? pensé que null está bien pero no es 0 en este caso según el ejemplo en el resumen. Por favor, confirme [RequiredNonDefault] public int? Test {get; conjunto; } – Krishna

+0

Eso es porque tu tipo es 'int ?. Y el valor predeterminado de cualquier objeto 'Nullable <>', es nulo, por lo que null no es válido, pero 0 es. Sin embargo, tienes razón en que mi ejemplo implica lo contrario. Lo arreglaré. Para que funcione lo que estás describiendo (permitiendo nulo, pero no 0), es mejor utilizar algo como esto '[Range (1, int.MaxValue)] int? i {get; set;} ' –

+0

@Albercht Gracias por la confirmación. – Krishna

3

Sé que esto es una vieja pregunta ahora, pero si alguien más está interesado logré evitar esto mediante la creación de una anotación [isNotEmpty] (haciendo la anulable no era Guid una opción en mi caso).

Esto usa la reflexión para determinar si hay una implementación de Empty en la propiedad, y si es así lo compara.

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

    } 
}