0

我刚刚进入自定义属性,我非常喜欢它们。我想知道是否可以创建一个应用于属性并表示同一对象中另一个属性的名称的属性。If 会检查被引用的属性是否有值,如果有,则需要修饰属性。像这样的东西:

[RequiredIfNotNull("ApprovedDate")]
[DisplayName("Approved By")]
[StringLength(50, ErrorMessage = "{0} must not exceed {1} characters")]
public string ApprovedBy { get; set; }

[DisplayName("Approved Date")]
[DisplayFormat(DataFormatString = "{0:d}")]
[PropertyMetadata(ColumnName = "ApprovedDate")]
public DateTime? ApprovedDate { get; set; }

因此,approved by 属性用RequiredIfNotNull 属性修饰,该属性引用该属性以检查是否为空。在这种情况下,批准日期。如果 ApprovedDate 有值,我希望 ApprovedBy 属性是必需的。有可能做这样的事情吗?如果是这样,您可以在服务器端和客户端实现它吗?

4

1 回答 1

1

这是我想出的: 服务器端:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Web.Mvc;   
namespace Atlas.Core.Attributes
{
    /// <summary>
    /// Add the following decoration: [ConditionalRequired("Model", "Field")]
    /// Model = client model being used to bind object
    /// Field = the field that if not null makes this field required.
    /// </summary>
    public class ConditionalRequiredAttribute : ValidationAttribute, IClientValidatable
    {
        private const string DefaultErrorMessageFormatString = "The {0} field is required.";
        private readonly string _dependentPropertyPrefix;
        private readonly string _dependentPropertyName;

        public ConditionalRequiredAttribute(string dependentPropertyPrefix, string dependentPropertyName)
        {
            _dependentPropertyPrefix = dependentPropertyPrefix;
            _dependentPropertyName = dependentPropertyName;
            ErrorMessage = DefaultErrorMessageFormatString;
        }

        protected override ValidationResult IsValid(object item, ValidationContext validationContext)
        {
            PropertyInfo property = validationContext.ObjectInstance.GetType().GetProperty(_dependentPropertyName);
            object dependentPropertyValue = property.GetValue(validationContext.ObjectInstance, null);

            if (dependentPropertyValue != null && item == null)
                return new ValidationResult(string.Format(ErrorMessageString, validationContext.DisplayName));

            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = string.Format("{0} is required", metadata.GetDisplayName()),
                ValidationType = "conditionalrequired",
            };

            rule.ValidationParameters.Add("requiredpropertyprefix", _dependentPropertyPrefix);
            rule.ValidationParameters.Add("requiredproperty", _dependentPropertyName);
            yield return rule;
        }
    }
}

客户端:

$.validator.unobtrusive.adapters.add('conditionalrequired', ['requiredpropertyprefix', 'requiredproperty'], function (options) {
        options.rules['conditionalrequired'] = {
            requiredpropertyprefix: options.params['requiredpropertyprefix'],
            requiredproperty: options.params['requiredproperty']
        };
        options.messages['conditionalrequired'] = options.message;
});

$.validator.addMethod('conditionalrequired', function (value, element, params) {
        var requiredpropertyprefix = params['requiredpropertyprefix'];
        var requiredproperty = params['requiredproperty'];
        var field = $('#' + requiredproperty).length == 0 ? '#' + requiredpropertyprefix + '_' + requiredproperty : '#' + requiredproperty;
        return !($(field).val().length > 0 && value.length == 0);
    }
);

I set this up to accept a model or prefix value and then the name of the actual field. The reason for this is that in many cases, I will add an object as part of a model, and that will cause the form id for that element to be rendered as ModelName_FieldName. But it also occurred to me that you may or may not use a model with an embedded object. In that case the id would just be FieldName, so the clientside code checks to see if the element exists by FieldName, and if not it returns ModelName_FieldName otherwise it just returns the FieldName. I didn't do it yet, but I should probably check to make sure the resulting fieldname has is not null.

and then to decorate your property you would do something like this:

[DataMember]
[DisplayName("Approved By")]
[ConditionalRequired("HOA", "ApprovedDate")]
[StringLength(50, ErrorMessage = "{0} must not exceed {1} characters")]
public string ApprovedBy { get; set; }

my model looks like this:

    public class HOAModel
    {
        public HOA HOA { get; set; }
   }

my view implementation looks like this:

Html.Kendo().DatePickerFor(m => m.HOA.ApprovedDate)

So my client side element has the following ID:

<input name="HOA.ApprovedDate" class="k-input" id="HOA_ApprovedDate" role="textbox">
于 2014-11-13T22:43:19.730 回答