【问题标题】:Writing a custom attribute with an infinite number of matching parameters编写具有无限个匹配参数的自定义属性
【发布时间】:2012-12-18 20:19:46
【问题描述】:

我编写了一个 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; }

【问题讨论】:

  • 是否接受Dictionary&lt;String.Object&gt;Tuple&lt;String,Object&gt; 的可枚举列表作为选项?您能否提供一个示例用法( 设想如何将此属性附加到模型)?
  • 我尝试使用 NameValueCollection,但出现编译器错误:“'nvc' 不是有效的命名属性参数,因为它不是有效的属性参数类型”。我认为其他集合类型也会有类似的问题,但没有专门尝试过。
  • 有趣,因为它应该是an acceptable type...
  • @Brad,不,只允许原始类型和它们的数组。字典和元组都不是。

标签: asp.net-mvc validation asp.net-mvc-2 data-annotations custom-attributes


【解决方案1】:

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

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

[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 属性做了类似的事情,如here 所示。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-26
    • 2022-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多