Let's say I have this view model:
public class MyModel
{
[Range(0, 999, ErrorMessage = "Invalid quantity")]
public int Quantity { get; set; }
}
Now, for specific instances of this model the range of valid values will change: some may not be 0, some may not be higher than 5. The min/max values for the valid ranges are coming from the DB and can change anytime.
How do I change the min/max properties of the RangeAttribute on the fly? Or what it the best way to validate my scenario?
Something along the lines of this might be more what your after...
ViewModel:
public class ViewModel
{
public DateTime MinDate {get; set;}
public DateTime MaxDate {get; set;}
[DynamicRange("MinDate", "MaxDate", ErrorMessage = "Value must be between {0} and {1}")]
public DateTime Date{ get; set; }
}
Library class or elsewhere:
public class DynamicRange : ValidationAttribute, IClientValidatable
{
private readonly string _minPropertyName;
private readonly string _maxPropertyName;
public DynamicRange(string minPropName, string maxPropName)
{
_minPropertyName = minPropName;
_maxPropertyName = maxPropName;
}
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));
var minValue = (int) minProperty.GetValue(validationContext.ObjectInstance, null);
var maxValue = (int) maxProperty.GetValue(validationContext.ObjectInstance, null);
var 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 = ErrorMessage
};
rule.ValidationParameters["minvalueproperty"] = _minPropertyName;
rule.ValidationParameters["maxvalueproperty"] = _maxPropertyName;
yield return rule;
}
From: MVC unobtrusive range validation of dynamic values
I think your best bet might be to implement a custom model binder for your specific Model (MyModel
). What you could have is something like this:
public class MyModel
{
public int Quantity { get; set; }
} // unchanged Model
public class MyViewModel
{
public MyModel myModel { get; set; }
public int QuantityMin { get; set; }
public int QuantityMax { get; set; }
}
Then you can set these values, and in your custom model binder you can compare your myModel.Quantity
property to the QuantityMin
and QuantityMax
properties.
Example
Model:
public class QuantityModel
{
public int Quantity { get; set; }
}
ViewMode:
public class QuantityViewModel
{
public QuantityModel quantityModel { get; set; }
public int QuantityMin { get; set; }
public int QuantityMax { get; set; }
}
Custom Model Binder:
public class VarQuantity : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
int MinValue = Convert.ToInt32(bindingContext.ValueProvider.GetValue("QuantityMin").AttemptedValue);
int MaxValue = Convert.ToInt32(bindingContext.ValueProvider.GetValue("QuantityMax").AttemptedValue);
int QuantityValue = Convert.ToInt32(bindingContext.ValueProvider.GetValue("quantityModel.Quantity").AttemptedValue);
if (!(QuantityValue >= MinValue && QuantityValue <= MaxValue))
bindingContext.ModelState.AddModelError("Quantity", "Quantity not between values");
return bindingContext.Model;
}
}
Register Custom Model Binder:
ModelBinders.Binders.Add(typeof(QuantityViewModel), new VarQuantity());
Test Controller Action Methods:
public ActionResult Quantity()
{
return View();
}
[HttpPost]
public string Quantity(QuantityViewModel qvm)
{
if (ModelState.IsValid)
return "Valid!";
else
return "Invalid!";
}
Test View Code:
@model MvcTest.Models.QuantityViewModel
<h2>Quantity</h2>
@using (Html.BeginForm())
{
@Html.Label("Enter Your Quantity: ")
@Html.TextBoxFor(m => m.quantityModel.Quantity)
<br />
@Html.Label("Quantity Minimum: ")
@Html.TextBoxFor(m => m.QuantityMin)
<br />
@Html.Label("Quantity Maximum: ")
@Html.TextBoxFor(m => m.QuantityMax)
<br /><br />
<input type="submit" value="Submit" />
}