1

我编写了一个 If-IsRequired 自定义属性来验证一个属性是否包含一个值,具体取决于模型中其他一些属性的值。由于我想让这个属性适用于尽可能多的情况,我想允许开发人员选择利用该属性来提供无限数量的匹配参数。最后,我希望能够强制所有参数都正确匹配。

这是我迄今为止所写的。虽然我目前正在使用字符串数组,但我很乐意使用某种无法工作的集合。此外,我现在需要支持当前的属性定义并创建一个包含比较运算符的新重载。除了假设所有比较都使用等于完成的原始定义之外,这将允许我进行小于、大于和不相等的比较。

    /// <summary>
    /// A custom attribute that checks the value of other properties passed to it in order to
    /// determine if the property this attribute is bound to should be required.
    /// </summary>
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
    public class IsPropertyRequiredAttribute : ValidationAttribute
    {
        private const string DefaultErrorMessage = "{0} is required.";

        public string[] _selectionContextNames { get; private set; }
        public string[] _expectedValues { get; private set; }

        /// <summary>
        /// Creates a new instance of the IsPropertyRequriedAttribute.
        /// </summary>
        /// <param name="SelectionContextNames">The name of the other property in the view model to check the value of.</param>
        /// <param name="ExpectedValues">The expected value of the other property in the view model in order to determine if the current property the attribute is bound to should be required.</param>
        public IsPropertyRequiredAttribute(string[] SelectionContextNames, string ExpectedValues)
            : base(DefaultErrorMessage)
        {

            _selectionContextNames = SelectionContextNames;
            _expectedValues = ExpectedValues;
        }

        public override bool IsValid(object value)
        {
            if (_selectionContextNames == null || _expectedValues == null)
            {
                if (_selectionContextNames != null || _expectedValues != null)
                {
                    string paramName;
                    if (_selectionContextNames == null)
                    {
                        paramName = "ExpectedValues";
                    }
                    else
                    {
                        paramName = "SelectionContextNames";
                    }

                    throw new ArgumentException("Key/Value pairs need to match for IsPropertyRequired.", paramName);
                }
            }
            else if (_selectionContextNames.Length != _expectedValues.Length)
            {
                string paramName;
                if (_selectionContextNames.Length < _expectedValues.Length)
                {
                    paramName = "ExpectedValues";
                }
                else
                {
                    paramName = "SelectionContextNames";
                }

                throw new ArgumentException("Parameter element counts need to match for IsPropertyRequired.", paramName);
            }

            bool paramsValid = true;

            if (_selectionContextName!= null)
            {
                for (int i = 0; i < _selectionContextName.Length; i++)
                {
                    string paramValue = HttpContext.Current.Request[_selectionContextName[i]];

                    if (_expectedValue[i] != paramValue)
                    {
                        paramsValid = false;
                    }
                }

                if (paramsValid == true)
                {
                    return (value != null);
                }
                else
                {
                    return true;
                }
            }
            else
            {
                return true;
            }
        }

        public override string FormatErrorMessage(string name)
        {
            return String.Format(DefaultErrorMessage, name);
        }
    }

虽然使用属性来装饰属性将取决于属性的定义方式,这是我目前实现的(也可能会改进):

[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4"}, new string[] {"1", "2", "3", "4"})]
public string SomeText { get; set; }

另外,我想尽可能地防止以下装饰的发生:

[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4", "prop5withoutvalue"}, new string[] {"1", "2", "3", "4"})]
public string SomeOtherText { get; set; }

并且使用包含比较运算符作为参数的新重载,我们现在可以拥有:

[IsPropertyRequired(new string[] {"prop1", "prop2", "prop3", "prop4"}, new string[] {"==", ">", "!=", "<="}, new string[] {"1", "2", "3", "4"})]
public string SomeComparisonText { get; set; }
4

1 回答 1

0

如MSDN中所述,.NET 中的属性在您可以指定的允许类型方面非常有限。如果您想指定更复杂的数据,我建议您编写属性来为更丰富的数据结构指定备用位置。

例如,想象一个具有以下语法的属性:

[ValidationRules(typeof(MyValidationRuleInfo, "MyRuleSet")]
public int SomeProperty { get; set; }

...

public static class MyValidationRuleInfo {
    public static Dictionary<string, ValidationRule> MyRuleSet {
        get {
            return new { ... rules go here ... }
        }
}

该属性将在目标类上查找属性并在那里获取所有规则。实现所有规则的所有逻辑仍然取决于您,但您可以避免使用属性汤,也可以避免笨拙的数据结构。

实际上,xUnit.NET 单元测试库对其TheoryPropertyData属性做了类似的事情,如下所示

于 2013-03-13T01:33:28.807 回答