Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是否可以将模型特性用作范围验证属性的一部分_C#_Asp.net Mvc - Fatal编程技术网

C# 是否可以将模型特性用作范围验证属性的一部分

C# 是否可以将模型特性用作范围验证属性的一部分,c#,asp.net-mvc,C#,Asp.net Mvc,在我的模型中,我有一个具有以下属性的对象 [Range(typeof(int), "2014", "2024", ErrorMessage = "{0} can only be beteween {1} and {2}")] public int FiscalYear { get; set; } 下限值和上限值分别为2014年和2024年。但是,我希望它们基于模型中的另一个属性,而不是使用这些固定值 例如,如果我有一个属性,CurrentFiscalYear,我假设的Range属性如下所示 [

在我的模型中,我有一个具有以下属性的对象

[Range(typeof(int), "2014", "2024", ErrorMessage = "{0} can only be beteween {1} and {2}")]
public int FiscalYear { get; set; }
下限值和上限值分别为2014年和2024年。但是,我希望它们基于模型中的另一个属性,而不是使用这些固定值

例如,如果我有一个属性,
CurrentFiscalYear
,我假设的
Range
属性如下所示

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

这样的事情可能吗?或者必须在编译时提供上下限值

这可以通过编写一个自定义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.";
    }
}

不,这是不可能的。属性参数值只能是“编译时常量”值。换句话说,编译程序时必须知道参数的实际值

发件人:

属性参数仅限于以下类型的常量值:

  • 简单类型(bool、byte、char、short、int、long、float和double)
  • 系统类型
  • 列举
  • 对象(object类型的属性参数的参数必须是上述类型之一的常量值。)
  • 上述任何一种类型的一维数组
这是.NET 1.1的文档,但尚未更改

解决方法

这根本不需要测试,但您可以创建一个自定义的
ValidationAttribute
,该属性接受范围,并在测试有效性时将属性名的值添加到范围值中。您可以创建一个内部标准
RangeAttribute
,为您完成这项工作,甚至通过实现以下功能来保持客户端验证正常工作:

如果您想让它与客户端验证一起工作,还需要创建并在global.asax
Application\u Start()
中注册它,以确保输出客户端验证HTML属性。同样,您可以作弊并使用内置的
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));

编辑:在测试后修复了一些错误

除了下面的答案之外,您还可以在模型中实现IValidatableObject接口,并在那里执行一些自定义逻辑/验证。这很好,但是我如何在客户端使用它呢
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);
    }
}
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));
[ShiftedRange(0, 10, "CurrentFiscalYear", "CurrentFiscalYear", ErrorMessage = "{0} can only be beteween {1} and {2}")]
public int FiscalYear { get; set; }