简体   繁体   English

我可以将Model属性用作Range验证属性的一部分

[英]Can I use a Model property as part of a Range validation attribute

In my model I have an object that has the following property. 在我的模型中,我有一个具有以下属性的对象。

[Range(typeof(int), "2014", "2024", ErrorMessage = "{0} can only be beteween {1} and {2}")]
public int FiscalYear { get; set; }

The lower and upper range values are 2014 and 2024 respectively. 下限和上限值分别为2014年和2024年。 However, rather than use these fixed values, I'd like them to be based on another property in the model. 但是,我不希望使用这些固定值,而是希望它们基于模型中的另一个属性。

So, for example, if I had a property, CurrentFiscalYear , my hypothetical Range attribute would look like this. 因此,例如,如果我有一个属性CurrentFiscalYear ,我的假设Range属性将如下所示。

[Range(typeof(int), CurrentFiscalYear, CurrentFiscalYear + 10, ErrorMessage = "{0} can only be beteween {1} and {2}")]
public int FiscalYear { get; set; }

Is something like this possible? 这样的事情可能吗? Or must the lower and upper values be provided at compile time? 或者必须在编译时提供较低和较高的值?

No, this isn't possible. 不,这是不可能的。 Attribute parameter values just be "compile-time constant" values. 属性参数值只是“编译时常量”值。 In other words, the actual value of the parameter must be known when you compile the program. 换句话说,编译程序时必须知道参数的实际值。

From MSDN - Attributes tutorial : 来自MSDN - 属性教程

Attribute parameters are restricted to constant values of the following types: 属性参数限制为以下类型的常量值:

  • Simple types (bool, byte, char, short, int, long, float, and double) 简单类型(bool,byte,char,short,int,long,float和double)
  • string
  • System.Type 系统类型
  • enums 枚举
  • object (The argument to an attribute parameter of type object must be a constant value of one of the above types.) object(对象类型的属性参数的参数必须是上述类型之一的常量值。)
  • One-dimensional arrays of any of the above types 任何上述类型的一维阵列

This is documentation for .NET 1.1, but has not changed. 这是.NET 1.1的文档,但没有改变。

Workaround 解决方法

This isn't tested at all but you can create a custom ValidationAttribute which takes the range and also model property names who's values to add to the range values when testing for validity. 这根本没有经过测试,但您可以创建一个自定义ValidationAttribute ,它在测试有效性时获取范围和模型属性名称,这些属性名称的值将添加到范围值中。 You can create an internal standard RangeAttribute to do the work for you and even keep client validation working by implementing IClientValidatable : 您可以创建内部标准RangeAttribute来为您完成工作,甚至通过实现IClientValidatable保持客户端验证的工作:

public sealed class ShiftedRangeAttribute : ValidationAttribute
{
    public string MinShiftProperty { get; private set; }
    public string MaxShiftProperty { get; private set; }

    public double Minimum { get; private set; }
    public double Maximum { get; private set; }

    public ShiftedRangeAttribute(double minimum, double maximum, string minShiftProperty, string maxShiftProperty)
    {
        this.Minimum = minimum;
        this.Maximum = maximum;
        this.MinShiftProperty = minShiftProperty;
        this.MaxShiftProperty = maxShiftProperty;
    }

    public ShiftedRangeAttribute(int minimum, int maximum, string minShiftProperty, string maxShiftProperty)
    {
        this.Minimum = minimum;
        this.Maximum = maximum;
        this.MinShiftProperty = minShiftProperty;
        this.MaxShiftProperty = maxShiftProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        RangeAttribute attr = this.CreateRangeAttribute(validationContext.ObjectInstance);
        return attr.GetValidationResult(value, validationContext);
    }

    internal RangeAttribute CreateRangeAttribute(object model)
    {
        double min = this.Minimum;
        if (this.MinShiftProperty != null)
        {
            min += Convert.ToDouble(model.GetType().GetProperty(this.MinShiftProperty).GetValue(model));
        }

        double max = this.Maximum;
        if (this.MaxShiftProperty != null)
        {
            max += Convert.ToDouble(model.GetType().GetProperty(this.MaxShiftProperty).GetValue(model));
        }

        return new RangeAttribute(min, max);
    }
}

If you want it to work with client validation, you will also to create a DataAnnotationsModelValidator and register it in your global.asax Application_Start() to ensure the client validation HTML attributes are output. 如果您希望它与客户端验证一起使用,您还将创建DataAnnotationsModelValidator并在global.asax Application_Start()注册它,以确保输出客户端验证HTML属性。 Again you can cheat and use the built-in RangeAttributeAdapter to help you because in Javascript it is ultimately just a range validator: 你可以再次欺骗并使用内置的RangeAttributeAdapter来帮助你,因为在Javascript中它最终只是一个范围验证器:

public class ShiftedRangeAttributeAdapter : DataAnnotationsModelValidator<ShiftedRangeAttribute>
{
    public ShiftedRangeAttributeAdapter(ModelMetadata metadata, ControllerContext context, ShiftedRangeAttribute attribute)
        : base(metadata, context, attribute)
    {
    }

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
    {
        RangeAttribute attr = this.Attribute.CreateRangeAttribute(this.Metadata.Container);
        return new RangeAttributeAdapter(this.Metadata, this.ControllerContext, attr).GetClientValidationRules();
    }
}

...

DataAnnotationsModelValidatorProvider.RegisterAdapter(
    typeof(ShiftedRangeAttribute), typeof(ShiftedRangeAttributeAdapter));

Note that the client validation code only works if the class containing the properties is the top-level model class, which is stored in Metadata.Container . 请注意,只有包含属性的类是顶级模型类(存储在Metadata.Container中)时,客户端验证代码才有效。 You cannot access the "parent" of the current property. 您无法访问当前属性的“父级”。 You would need to do more work to create a custom jQuery validator to handle this properly. 您需要做更多的工作来创建一个自定义的jQuery验证器来正确处理这个问题。

You can then use it as so: 然后你可以这样使用它:

[ShiftedRange(0, 10, "CurrentFiscalYear", "CurrentFiscalYear", ErrorMessage = "{0} can only be beteween {1} and {2}")]
public int FiscalYear { get; set; }

EDIT: fixed some bugs after testing 编辑:测试后修复了一些错误

This can be done by writing a custom ValidationAttribute, implementation could be done something like this: 这可以通过编写自定义ValidationAttribute来完成,实现可以这样完成:

public sealed class FiscalYearAttribute : ValidationAttribute
{
    public string CurrentFiscalYear { get; set; }

    public override bool IsValid(object value)
    {
        var currentFiscalYearString = HttpContext.Current.Request[CurrentFiscalYear];
        var currentFiscalYear = int.Parse(currentFiscalYearString);
        var fiscalYear = (int) value;

        return fiscalYear >=  currentFiscalYear && fiscalYear <= currentFiscalYear + 10;
    }

    public override string FormatErrorMessage(string name)
    {
        return name + " error description here.";
    }
}

Usage: 用法:

[Required]
[Display(Name = "CurrentFiscalYear")]
public int CurrentFiscalYear { get; set; }

[Display(Name = "FiscalYear")]
[FiscalYear(CurrentFiscalYear = "CurrentFiscalYear")]
public int FiscalYear { get; set; }

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

相关问题 我可以在Json序列化器的模型的单个属性上使用TypeConverter属性吗? - Can I use TypeConverter attribute on single property of a model for Json serializer? 我可以使用自定义验证属性手动验证属性吗? - Can I manually validate a property using a custom validation attribute? 如何在Web API中的模型中禁用属性的必填属性? - How can I disable required attribute on a property in a model in web api? 使用IModelValidator创建自定义属性验证属性 - use IModelValidator to Create a Custom Property Validation Attribute 我可以在自定义验证属性的定义中使用依赖项注入吗? - Can I use dependency injection in the definition of a custom validation attribute? 如何在条件验证中使用相同的属性 - How can I use same property with conditional validation 如何在C#中的自我属性对象中使用验证? - How can I use validation in self property object in C#? ASP Net Core模型验证Range属性被忽略 - ASP Net Core model validation Range attribute is ignored 如何为属性的Description属性使用resorce? - How can I use resorce for Description attribute for a property? 我可以在属性上使用属性来告诉DataGridView如何格式化列吗? - Is there an Attribute I can use on a property to tell DataGridView how to format the column?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM