16

Ho un valore sul mio modello, che deve rientrare nell'intervallo di altri due valori sul mio modello.MVC convalida intervallo non invadente di valori dinamici

Ad esempio:

public class RangeValidationSampleModel 
{ 
    int Value { get; set; } 

    int MinValue { get; set; } 

    int MaxValue { get; set; } 
} 

Naturalmente, non posso passare questi Min/MaxValues ​​nelle mie DataAnnotations attributi, in quanto devono essere valori costanti.

Sono sicuro di aver bisogno di creare il mio attributo di convalida, ma non l'ho fatto molto e non riesco a spiegarmi come dovrebbe funzionare.

Ho cercato per circa un'ora, e ho visto tutti i tipi di soluzioni per la creazione di convalida personalizzata, ma non riesco a trovare nulla per risolvere questo particolare problema utilizzando MVC3 convalida discreto.

+0

deve essere convalida lato client? –

+1

Sarebbe preferibile. Stiamo convertendo questo sito da MVC2 a MVC3 e attualmente la convalida MVC2 sta funzionando sul lato client, quindi mi piacerebbe continuare a farlo funzionare in questo modo. Ma mi piacerebbe usare la convalida non invadente, se possibile. La validazione attuale è molto invadente. :) –

risposta

34

Si potrebbe scrivere un attributo di convalida personalizzato per questo scopo:

public class DynamicRangeValidator : ValidationAttribute, IClientValidatable 
{ 
    private readonly string _minPropertyName; 
    private readonly string _maxPropertyName; 
    public DynamicRangeValidator(string minPropertyName, string maxPropertyName) 
    { 
     _minPropertyName = minPropertyName; 
     _maxPropertyName = maxPropertyName; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var minProperty = validationContext.ObjectType.GetProperty(_minPropertyName); 
     var maxProperty = validationContext.ObjectType.GetProperty(_maxPropertyName); 
     if (minProperty == null) 
     { 
      return new ValidationResult(string.Format("Unknown property {0}", _minPropertyName)); 
     } 
     if (maxProperty == null) 
     { 
      return new ValidationResult(string.Format("Unknown property {0}", _maxPropertyName)); 
     } 

     int minValue = (int)minProperty.GetValue(validationContext.ObjectInstance, null); 
     int maxValue = (int)maxProperty.GetValue(validationContext.ObjectInstance, null); 
     int currentValue = (int)value; 
     if (currentValue <= minValue || currentValue >= maxValue) 
     { 
      return new ValidationResult(
       string.Format(
        ErrorMessage, 
        minValue, 
        maxValue 
       ) 
      ); 
     } 

     return null; 
    } 

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
    { 
     var rule = new ModelClientValidationRule 
     { 
      ValidationType = "dynamicrange", 
      ErrorMessage = this.ErrorMessage, 
     }; 
     rule.ValidationParameters["minvalueproperty"] = _minPropertyName; 
     rule.ValidationParameters["maxvalueproperty"] = _maxPropertyName; 
     yield return rule; 
    } 
} 

e poi decorare il vostro modello di vista con esso:

public class RangeValidationSampleModel 
{ 
    [DynamicRangeValidator("MinValue", "MaxValue", ErrorMessage = "Value must be between {0} and {1}")] 
    public int Value { get; set; } 
    public int MinValue { get; set; } 
    public int MaxValue { get; set; } 
} 

allora si potrebbe avere un controller che serve una visione:

public class HomeController : Controller 
{ 
    public ActionResult Index() 
    { 
     return View(new RangeValidationSampleModel 
     { 
      Value = 5, 
      MinValue = 6, 
      MaxValue = 8 
     }); 
    } 

    [HttpPost] 
    public ActionResult Index(RangeValidationSampleModel model) 
    { 
     return View(model); 
    } 
} 

e una vista del corso:

@model RangeValidationSampleModel 

<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script> 
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script> 
<script type="text/javascript"> 
    $.validator.unobtrusive.adapters.add('dynamicrange', ['minvalueproperty', 'maxvalueproperty'], 
     function (options) { 
      options.rules['dynamicrange'] = options.params; 
      if (options.message != null) { 
       $.validator.messages.dynamicrange = options.message; 
      } 
     } 
    ); 

    $.validator.addMethod('dynamicrange', function (value, element, params) { 
     var minValue = parseInt($('input[name="' + params.minvalueproperty + '"]').val(), 10); 
     var maxValue = parseInt($('input[name="' + params.maxvalueproperty + '"]').val(), 10); 
     var currentValue = parseInt(value, 10); 
     if (isNaN(minValue) || isNaN(maxValue) || isNaN(currentValue) || minValue >= currentValue || currentValue >= maxValue) { 
      var message = $(element).attr('data-val-dynamicrange'); 
      $.validator.messages.dynamicrange = $.validator.format(message, minValue, maxValue); 
      return false; 
     } 
     return true; 
    }, ''); 
</script> 

@using (Html.BeginForm()) 
{ 
    <div> 
     @Html.LabelFor(x => x.Value) 
     @Html.EditorFor(x => x.Value) 
     @Html.ValidationMessageFor(x => x.Value) 
    </div> 
    <div> 
     @Html.LabelFor(x => x.MinValue) 
     @Html.EditorFor(x => x.MinValue) 
    </div> 
    <div> 
     @Html.LabelFor(x => x.MaxValue) 
     @Html.EditorFor(x => x.MaxValue) 
    </div> 
    <button type="submit">OK</button> 
} 

Ovviamente la registrazione adattatore personalizzato deve essere effettuata in un file JavaScript esterno per evitare di inquinare la vista, ma per lo scopo e concisione di questo post ho messo dentro la vista.

+1

Grazie Darin, è fantastico! Ho dovuto fare due piccole modifiche al codice javascript. I controlli 'isNaN' erano invertiti (dovevano rimuovere'! '), E ho anche spostato il controllo min/max w/i controlli' isNaN' in modo che il messaggio venisse impostato correttamente. –

+0

@JeradRose, infatti, il mio codice ha avuto un errore. L'ho riparato ora. Non dovresti esitare ad aggiornare la mia risposta anche se vedi problemi con esso. –

+0

grazie per quello. Ho preso in considerazione la possibilità di modificarlo, ma sembra che non disponga delle autorizzazioni per farlo (penso che sia in 2k). –

2

attributi di convalida personalizzati sono davvero un buon pensiero. qualcosa di simile (a scavare un po 'frammento O'Mine trovato chissà dove qualche tempo fa):

public sealed class MustBeGreaterThan : ValidationAttribute 
{ 
    private const string _defaultErrorMessage = "'{0}' must be greater than '{1}'"; 
    private string _basePropertyName; 

    public MustBeGreaterThan(string basePropertyName) 
     : base(_defaultErrorMessage) 
    { 
     _basePropertyName = basePropertyName; 
    } 

    //Override default FormatErrorMessage Method 
    public override string FormatErrorMessage(string name) 
    { 
     return string.Format(_defaultErrorMessage, name, _basePropertyName); 
    } 

    //Override IsValid 
    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var basePropertyInfo = validationContext.ObjectType.GetProperty(_basePropertyName); 
     var lowerBound = (int)basePropertyInfo.GetValue(validationContext.ObjectInstance, null); 
     var thisValue = (int)value; 

     if (thisValue < lowerBound) 
     { 
      var message = FormatErrorMessage(validationContext.DisplayName); 
      return new ValidationResult(message); 
     } 

     //value validated 
     return null; 
    } 
} 

public sealed class MustBeLowerThan : ValidationAttribute 
{ 
    private const string _defaultErrorMessage = "'{0}' must be lower than '{1}'"; 
    private string _basePropertyName; 

    public MustBeLowerThan(string basePropertyName) 
     : base(_defaultErrorMessage) 
    { 
     _basePropertyName = basePropertyName; 
    } 

    //Override default FormatErrorMessage Method 
    public override string FormatErrorMessage(string name) 
    { 
     return string.Format(_defaultErrorMessage, name, _basePropertyName); 
    } 

    //Override IsValid 
    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var basePropertyInfo = validationContext.ObjectType.GetProperty(_basePropertyName); 
     var upperBound = (int)basePropertyInfo.GetValue(validationContext.ObjectInstance, null); 
     var thisValue = (int)value; 

     if (thisValue > upperBound) 
     { 
      var message = FormatErrorMessage(validationContext.DisplayName); 
      return new ValidationResult(message); 
     } 

     //value validated 
     return null; 
    } 
} 

poi decorare la vostra classe

public class RangeValidationSampleModel 
{ 
    [MustBeGreaterThan("MinValue")] 
    [MustBeLowerThan("MaxValue")] 
    int Value { get; set; } 

    int MinValue { get; set; } 

    int MaxValue { get; set; } 
} 

e si dovrebbe essere pronti per partire

+0

Grazie a @alex. Questo non sembra che funzionerebbe con la convalida discreta lato client, però, lo farà? –

0

Se bisogno di convalida lato client questo dovrà essere personalizzato. Ho visto un bel post su qui di recente (Darin Dmitrov Cant sembra di trovarlo?) In ogni modo - questo permetterà di convalida del client si verifichi: http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx

lato server può essere gestito tramite IValidateableObject o Dynamic Range Validation in ASP.NET MVC 2

ecc ecc su il lato server, ma credo che tu voglia che il lato client sia la chiave.

Problemi correlati