听起来您的目标是“条件要求”规则,即根据另一个属性的值标记为必需的属性。仅使用 ValidationAttribute 是不可能的,因为该属性的范围仅限于它所装饰的属性。您需要实现 matchDataAnnotationsModelValidator
以具有更广泛的范围(ModelClientValidationRule
如果您希望它也验证客户端,还需要实现与关联的客户端 javascript )。
因此,您的 ConditionalRequiredAttribute 看起来像:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class ConditionalRequiredAttribute : ValidationAttribute
{
public ConditionalRequiredAttribute(string triggerProperty, object triggerValue)
{
TriggerProperty = triggerProperty;
TriggerValue = triggerValue;
}
/// <summary>
/// Checks that the value of the decorated member is not null/empty if the TriggerProperty's value is equal to TriggerValue.
/// </summary>
/// <param name="value">The data field value to validate.</param>
/// <returns>true if validation is successful; otherwise, false.</returns>
public override bool IsValid(object value)
{
if (value == null)
return false;
if (value is double)
{
return !((double)value).Equals(0);
}
string s = value as string;
if (s != null)
return s.Length > 0;
return true;
}
/// <summary>
/// The name of the property whose value will be checked to trigger the required field
/// </summary>
public string TriggerProperty { get; set; }
/// <summary>
/// The expected value of the trigger property that will trigger the required field
/// </summary>
public object TriggerValue { get; set; }
}
这实际上与标准属性几乎相同Required
- “特殊酱汁”实际上是验证器,您仅IsValid
在某些情况下使用它来调用方法(即TriggerProperty.Value
== TriggerValue
)。验证器看起来像:
public class ConditionalRequiredValidator : DataAnnotationsModelValidator<ConditionalRequiredAttribute>
{
public ConditionalRequiredValidator(ModelMetadata metadata, ControllerContext context,
ConditionalRequiredAttribute attribute)
: base(metadata, context, attribute)
{
}
/// <summary>
/// Override the default validate method to only execute if the TriggerProperty's value is equal to TriggerValue
/// </summary>
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// Does the specified property exist in the metadata?
PropertyInfo triggerProperty = Metadata.ContainerType.GetProperty(Attribute.TriggerProperty);
if (triggerProperty != null)
{
object actualValue = triggerProperty.GetValue(container, null);
if (actualValue != null)
{
if (Attribute.TriggerValue.Equals(actualValue))
{
// Run IsValid for the property if the actual value matches the expected value
foreach (ModelValidationResult result in base.Validate(container))
{
yield return result;
}
}
}
}
}
}
最后,您需要向ConditionalRequiredValidator
提供者注册,以确保框架在ConditionalRequiredAttribute
使用 a 时更喜欢它。为此,请在Application_Start()
Global.asax.cs 的方法中添加以下行:
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(ConditionalRequiredAttribute), typeof(ConditionalRequiredValidator));
然后你像这样装饰粒子类(或者实际上是任何其他类)的成员:
public class Particle
{
// Mass is required; easy enough.
[Required(ErrorMessage="Mass is required.")]
public double Mass { get; set; }
[ConditionalRequired("Momentum", 0D, ErrorMessage = "Position must be set if Momentum is not.")]
public double Position { get; set; }
[ConditionalRequired("Position", 0D, ErrorMessage = "Momentum must be set if Position is not.")]
public double Momentum { get; set; }
}
瞧,您现在应该能够根据另一个字段的值有条件地验证一个字段。
在旁注中,您可以将此条件逻辑抽象为一个辅助类并创建一系列“条件”验证器,ConditionalRequired,ConditionalRange 等......
注意 2:虽然它可能比您自己的解决方案更复杂/“更多代码”(在我仍在整理此回复时发布 - doh!)这确实具有非常可重用的好处。要将相同的功能添加到未来的视图模型中,您只需使用属性装饰您的ConditionalRequired
属性...