我可以使用模型属性作为范围验证属性的一部分吗
Can I use a Model property as part of a Range validation attribute
在我的模型中,我有一个具有以下属性的对象。
[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; }
这样的事情可能吗?或者必须在编译时提供 lower 和 upper 值?
这可以通过编写自定义 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.";
}
}
用法:
[Required]
[Display(Name = "CurrentFiscalYear")]
public int CurrentFiscalYear { get; set; }
[Display(Name = "FiscalYear")]
[FiscalYear(CurrentFiscalYear = "CurrentFiscalYear")]
public int FiscalYear { get; set; }
不,这不可能。属性参数值只是 "compile-time constant" 值。也就是说,在编译程序的时候,必须知道参数的实际值。
Attribute parameters are restricted to constant values of the following types:
- Simple types (bool, byte, char, short, int, long, float, and 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.)
- One-dimensional arrays of any of the above types
这是 .NET 1.1 的文档,但没有更改。
解决方法
这根本没有经过测试,但您可以创建一个自定义 ValidationAttribute
,它采用范围和模型 属性 在测试有效性时要添加到范围值的值的名称。您可以创建一个内部标准 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);
}
}
如果您希望它与客户端验证一起使用,您还需要创建一个 DataAnnotationsModelValidator
并将其注册到您的 global.asax Application_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));
请注意,仅当包含属性的 class 是存储在 Metadata.Container
中的顶级模型 class 时,客户端验证代码才有效。您无法访问当前 属性 的 "parent"。您需要做更多工作来创建自定义 jQuery 验证器来正确处理此问题。
然后您可以这样使用它:
[ShiftedRange(0, 10, "CurrentFiscalYear", "CurrentFiscalYear", ErrorMessage = "{0} can only be beteween {1} and {2}")]
public int FiscalYear { get; set; }
编辑:测试后修复了一些错误
在我的模型中,我有一个具有以下属性的对象。
[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; }
这样的事情可能吗?或者必须在编译时提供 lower 和 upper 值?
这可以通过编写自定义 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.";
}
}
用法:
[Required]
[Display(Name = "CurrentFiscalYear")]
public int CurrentFiscalYear { get; set; }
[Display(Name = "FiscalYear")]
[FiscalYear(CurrentFiscalYear = "CurrentFiscalYear")]
public int FiscalYear { get; set; }
不,这不可能。属性参数值只是 "compile-time constant" 值。也就是说,在编译程序的时候,必须知道参数的实际值。
Attribute parameters are restricted to constant values of the following types:
- Simple types (bool, byte, char, short, int, long, float, and 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.)
- One-dimensional arrays of any of the above types
这是 .NET 1.1 的文档,但没有更改。
解决方法
这根本没有经过测试,但您可以创建一个自定义 ValidationAttribute
,它采用范围和模型 属性 在测试有效性时要添加到范围值的值的名称。您可以创建一个内部标准 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);
}
}
如果您希望它与客户端验证一起使用,您还需要创建一个 DataAnnotationsModelValidator
并将其注册到您的 global.asax Application_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));
请注意,仅当包含属性的 class 是存储在 Metadata.Container
中的顶级模型 class 时,客户端验证代码才有效。您无法访问当前 属性 的 "parent"。您需要做更多工作来创建自定义 jQuery 验证器来正确处理此问题。
然后您可以这样使用它:
[ShiftedRange(0, 10, "CurrentFiscalYear", "CurrentFiscalYear", ErrorMessage = "{0} can only be beteween {1} and {2}")]
public int FiscalYear { get; set; }
编辑:测试后修复了一些错误