Ненавязчивая проверка C # MVC Razor

Можно ли провести ненавязчивую проверку, чтобы сделать поле обязательным, но только в случае изменения других свойств?

Например

[Required]
public Decimal Income {get; set;}
[Required]
public Decimal Tax {get; set;}
//Required if tax or income changes
public string ChangeReason {get; set;}

Я думал о том, чтобы иметь несколько полей для хранения резервных копий и написать собственный валидатор, чтобы сравнить их, но подумал, есть ли у кого-нибудь лучшее предложение?

 JP Del Mundo15 июн. 2012 г., 16:34
Вот аналогичный вопрос с решением:stackoverflow.com/questions/2417113/…

Ответы на вопрос(2)

Решение Вопроса

Пользовательский валидатор это путь. Я должен был построить нечто подобное некоторое время назад.

Я установил скрытое значение - "Изменено". - установите его в true всякий раз, когда пользователь изменяет интересующие вас поля.

Установите валидатор RequiredIf для двух интересующих вас свойств:

 [RequiredIf("Changed", true, ErrorMessage = "Required")]

Код для валидатора RequiredIf показан ниже:

public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
    private RequiredAttribute _innerAttribute = new RequiredAttribute();

    public string DependentProperty { get; set; }
    public object TargetValue { get; set; }

    public RequiredIfAttribute(string dependentProperty, object targetValue)
    {
        this.DependentProperty = dependentProperty;
        this.TargetValue = targetValue;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // get a reference to the property this validation depends upon
        var containerType = validationContext.ObjectInstance.GetType();
        var field = containerType.GetProperty(this.DependentProperty);

        if (field != null)
        {
            // get the value of the dependent property
            var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);

            // compare the value against the target value
            if ((dependentvalue == null && this.TargetValue == null) ||
                (dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
            {
                // match => means we should try validating this field
                if (!_innerAttribute.IsValid(value))
                    // validation failed - return an error
                    return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName });
            }
        }

        return null;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule()
        {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "requiredif",
        };

        string depProp = BuildDependentPropertyId(metadata, context as ViewContext);

        // find the value on the control we depend on;
        // if it's a bool, format it javascript style 
        // (the default is True or False!)
        string targetValue = (this.TargetValue ?? "").ToString();
        if (this.TargetValue.GetType() == typeof(bool))
            targetValue = targetValue.ToLower();

        rule.ValidationParameters.Add("dependentproperty", depProp);
        rule.ValidationParameters.Add("targetvalue", targetValue);

        yield return rule;
    }

    private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
    {
        // build the ID of the property
        string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);
        // unfortunately this will have the name of the current field appended to the beginning,
        // because the TemplateInfo's context has had this fieldname appended to it. Instead, we
        // want to get the context as though it was one level higher (i.e. outside the current property,
        // which is the containing object (our Person), and hence the same level as the dependent property.
        var thisField = metadata.PropertyName + "_";
        if (depProp.StartsWith(thisField))
            // strip it off again
            depProp = depProp.Substring(thisField.Length);
        return depProp;
    }
}

Javascript:

/// <reference path="jquery-1.4.4-vsdoc.js" />
/// <reference path="jquery.validate.unobtrusive.js" />

$.validator.addMethod('requiredif',
function (value, element, parameters) {
    var id = '#' + parameters['dependentproperty'];

    // get the target value (as a string, 
    // as that's what actual value will be)
    var targetvalue = parameters['targetvalue'];
    targetvalue =
      (targetvalue == null ? '' : targetvalue).toString();

    // get the actual value of the target control
    // note - this probably needs to cater for more 
    // control types, e.g. radios
    var control = $(id);
    var controltype = control.attr('type');
    var actualvalue =
        controltype === 'checkbox' ?
        control.attr('checked').toString() :
        control.val();

    // if the condition is true, reuse the existing 
    // required field validator functionality
    if (targetvalue === actualvalue)
        return $.validator.methods.required.call(
          this, value, element, parameters);

    return true;
 }
 );

 $.validator.unobtrusive.adapters.add(
'requiredif',
['dependentproperty', 'targetvalue'],
function (options) {
    options.rules['requiredif'] = {
        dependentproperty: options.params['dependentproperty'],
        targetvalue: options.params['targetvalue']
    };
    options.messages['requiredif'] = options.message;
});
 24 авг. 2016 г., 02:38
Вы нашли способ использовать это с переключателями? Это имеет проблемы, поскольку MVC отображает дубликаты идентификаторов для переключателей. Для решения этой проблемы вручную нужно указать идентификаторы в представлении. Это изменение приводит к сбою этого валидатора, поскольку он не может найти ожидаемый элемент управления, используя идентификатор, сгенерированный вBuildDependentPropertyId... хрм: /
 06 июн. 2016 г., 16:27
Я исправил это, заменивdepProp = depProp.Substring(thisField.Length); сdepProp = depProp.Replace(thisField, "");
 02 дек. 2013 г., 21:41
Проверка на стороне клиента завершится неудачей, если вы используете вложенный редактор для помощников с этим кодом. Идентификатор зависимого свойства будет иметь префикс с более высокими именами свойств и этой строкой здесь:if (depProp.StartsWith(thisField)) всегда будет ложным Таким образом, зависимое свойство, передаваемое в качестве параметров проверки, будет неверным и, скорее всего, вызовет исключение, когда оно попытается проверить значение зависимого элемента управления.

Это возможно. Вы можете написать свой собственный атрибут, чтобы сделать это точно.
Это в основном требует двух шагов:

Write your own attribute, make it inherit ValidationAttribute amd implement IClientValidatable Write a Jquery validation adapter to support it

Хороший рабочий образец описан вэта почта.
 Я использовал аналогичный подход для создания проверки зависимости (одно поле может иметь значения, только если другое заполнено)

Ваш ответ на вопрос