MVC ненавязчивая проверка диапазона динамических значений
У меня есть значение в моей модели, которое должно находиться в диапазоне двух других значений в моей модели.
Например:
public class RangeValidationSampleModel
{
int Value { get; set; }
int MinValue { get; set; }
int MaxValue { get; set; }
}
Конечно, я не могу передать эти Min/MaxValues в мои атрибуты DataAnnotations, так как они должны быть постоянными значениями.
Я уверен, что мне нужно создать свой собственный атрибут проверки, но я так мало сделал и не могу понять, как это должно работать.
Я искал около часа и видел все виды решений для создания пользовательской проверки, но не могу найти ничего, чтобы решить эту конкретную проблему, используя ненавязчивую проверку MVC3.
3 ответа
Вы можете написать собственный атрибут проверки для этой цели:
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;
}
}
а затем украсьте им свою модель вида:
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; }
}
тогда у вас может быть контроллер, обслуживающий представление:
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);
}
}
и вид конечно:
@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>
}
Очевидно, что пользовательскую регистрацию адаптера следует выполнять во внешнем файле javascript, чтобы избежать загрязнения представления, но для цели и краткости этого поста я поместил его в представление.
Пользовательские атрибуты проверки действительно хорошая мысль. что-то вроде (выкопал какой-то фрагмент найденного, который знает где некоторое время назад):
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;
}
}
тогда украсьте свой класс
public class RangeValidationSampleModel
{
[MustBeGreaterThan("MinValue")]
[MustBeLowerThan("MaxValue")]
int Value { get; set; }
int MinValue { get; set; }
int MaxValue { get; set; }
}
и тебе надо идти
Если вам нужна проверка на стороне клиента, она должна быть индивидуальной. Недавно я увидел здесь хороший пост (Дарин Дмитров? Кажется, он не может его найти). В любом случае - это позволит проверить клиента: http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
Сторона сервера может обрабатываться с помощью IValidateableObject или проверки динамического диапазона в ASP.NET MVC 2.
и т. д. на стороне сервера, но я чувствую, что вы хотите, чтобы клиентская сторона была ключом.