简体   繁体   English

范围属性使用动态值而不是固定值

[英]Range attribute using dynamic values instead of fixed

I want to use Data annotations Range attribute inside my mvc viewmodel.我想在我的 mvc 视图模型中使用数据注释范围属性。 Problem is that this range attributes should be dynamic values.问题是这个范围属性应该是动态值。

My viewmodel has also ValueOne and ValueTwo properties.我的视图模型也有 ValueOne 和 ValueTwo 属性。 Based on this values I want to set Range attr.基于这个值,我想设置 Range attr。 values like

 [Range(1, 1000, ErrorMessage = "Value for {0} must be between {1} and {2}.")]

Where 1 and 1000 should be replaces with ValueOne and ValueTwo property values.其中 1 和 1000 应替换为 ValueOne 和 ValueTwo 属性值。

so I tried with custom ValidateCustomAttribute所以我尝试使用自定义 ValidateCustomAttribute

public class ValidateCustomAttribute: ValidationAttribute
    {
        private readonly double _MinValue = 0;
        private readonly double _MaxValue = 100;

        public override bool IsValid(object value)
        {
            double val = (double)value;
            return val >= _MinValue && val <= _MaxValue;
        }

        public override string FormatErrorMessage(string name)
        {
            return string.Format(ErrorMessage, _MinValue, _MaxValue);
        }
    }

how can I replace this我该如何替换这个

private readonly double _MinValue = 0;
private readonly double _MaxValue = 100;

with dynamic values (ValueOne and ValueTwo from my viewmodel).具有动态值(我的视图模型中的 ValueOne 和 ValueTwo)。

This can't be done, you can't have variables in attributes. 这不能做,你不能在属性中有变量。 Attribute values must be known at compile time. 必须在编译时知道属性值。

See this question/answer and this one. 看到这个问题/答案一个。

Just add a constructor: 只需添加一个构造函数:

private double _MinValue, _MaxValue; // no readonly keyword

public ValidateCustomAttribute(double min, double max, Func<string> errorMessageAccessor)
    : base(errorMessageAccessor)
{
    _MinValue = min;
    _MaxValue = max;
}

What you can't do is have variables in the attribute constructor invokation. 你不能做的是在属性构造函数invokation中有变量。

This is not possible: 这是不可能的:

[ValidateCustom(min, max)]

But if you use literals (or constants) in your code you can have these: 但是如果你在代码中使用文字(或常量),你可以拥有:

[ValidateCustom(1, 1000)]

And on another class or method: 在另一个类或方法上:

[ValidateCustom(3, 45)]

What you are missing is the constructor taking in those static values and affixing them to the construct you are describing with your attribute. 您缺少的是构造函数接收这些静态值并将它们附加到您使用属性描述的构造中。


EDIT: The ugly way around this 编辑:围绕这个丑陋的方式

If you really really need this, you can circumvent the limitation but it is as ugly as it can get. 如果你真的需要这个,你可以规避限制,但它可以得到它的丑陋。 I am STRONGLY against this, but you asked for it... 强烈反对这一点,但你要求它......

  1. categorize your data 对您的数据进行分类
  2. map categories to binding symbols 将类别映射到绑定符号
  3. use binding symbols 使用绑定符号
  4. resolve binding symbols to data 将绑定符号解析为数据

So, let's get to work: 那么,让我们开始工作:

1) categorize your data 1)对数据进行分类

Say, your data is a range (min, max) , the first thing to do is establish which values are possible, let's say you have 4 possible ranges (may be hundreds, but that's anther problem altogether). 比如说,你的数据是一个范围(min, max) ,首先要做的是确定哪些值是可能的,假设你有4个可能的范围(可能是数百个,但这完全是另一个问题)。

(1, 1000)
(10, 20)
(3, 45)
(5, 7)

2) map categories to binding symbols 2)将类别映射到绑定符号

Now you have to use an enum as binding symbols for those ranges: 现在,您必须使用enum作为这些范围的绑定符号:

public enum MyRanges
{
    R1, R2, R3, R4
}

3) use binding symbols 3)使用绑定符号

Define the constructor as taking in the binding symbol: 将构造函数定义为接受绑定符号:

private MyRanges _R;

public ValidateCustomAttribute(MyRanges r, Func<string> errorMessageAccessor)
    : base(errorMessageAccessor)
{
    _R = r;
}

The attribute will be used like this: 该属性将使用如下:

[ValidateCustom(MyRanges.R2, "ERROR!")]

4) resolve binding symbols to data 4)解析绑定符号到数据

The last you need is a dictionary with the actual data: 您需要的最后一个是包含实际数据的字典:

Dictionary<MyRanges, double> dataMin = {
    { MyRanges.R1, 1},
    { MyRanges.R2, 10},
    { MyRanges.R3, 3},
    { MyRanges.R4, 5}
};

Dictionary<MyRanges, double> dataMax = {
    { MyRanges.R1, 1000},
    { MyRanges.R2, 20},
    { MyRanges.R3, 45},
    { MyRanges.R4, 7}
};

The test will use the binding this way: 测试将以这种方式使用绑定:

public override bool IsValid(object value)
{
    double val = (double)value;
    return val >= dataMin[_R] && val <= dataMax[_R]; // get data through binding
}

Now you can change behind the scenes those values and all attributes bound by the binding symbols behave differently: 现在,您可以在幕后更改这些值,并且绑定符号绑定的所有属性的行为都不同:

dataMax[MyRanges.R4] = 29;

Done. 完成。

What cannot change is now the binding from attribute to category, but the data contained in the category is free to change. 现在无法更改的是从属性到类别的绑定,但类别中包含的数据可以自由更改。

But ugly and impossible to maintain. 但丑陋且无法维持。 Don't do it, really. 真的,不要这样做。

You can do it like this: 你可以这样做:

public class MinimumAgeAttribute : RangeAttribute
{
    public static string MinimumValue => ConfigurationManager.AppSettings["your key"];
    public static string MaxValue => ConfigurationManager.AppSettings["your key"];

    public CustomRangeAttribute(Type type):base(type,MaxValue , MinimumValue)
    {
    }

}

And after this you have to register the attribute in Global.asax like this: 在此之后,您必须在Global.asax中注册属性,如下所示:

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CustomRangeAttribute),
            typeof(RangeAttributeAdapter));

After this you can use it like this: 在此之后你可以像这样使用它:

[CustomRange(typeof(DateTime), ErrorMessage = "You message")]
public DateTime DateOfBirth { get; set; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM