1

我有以下的定制要求的属性:用C#和Web API,并使用与验证方面的私人访问修饰符定制所需的属性

public class RequiredIfAttribute : RequiredAttribute 
{ 
    private string _DependentProperty; 
    private object _TargetValue; 

    public RequiredIfAttribute(string dependentProperty, object targetValue) 
    { 
     this._DependentProperty = dependentProperty; 
     this._TargetValue = targetValue; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var propertyTestedInfo = validationContext.ObjectType.GetProperty(this._DependentProperty); 

     if (propertyTestedInfo == null) 
     { 
      return new ValidationResult(string.Format("{0} needs to be exist in this object.", this._DependentProperty)); 
     } 

     var dependendValue = propertyTestedInfo.GetValue(validationContext.ObjectInstance, null); 

     if (dependendValue == null) 
     { 
      return new ValidationResult(string.Format("{0} needs to be populated.", this._DependentProperty)); 
     } 

     if (dependendValue.Equals(this._TargetValue)) 
     { 
      var x = validationContext.ObjectType.GetProperty("_Mappings"); 

      var objectInstance = (Dictionary<object, string[]>)x.GetValue(validationContext.ObjectInstance, null); 

      var isRequiredSatisfied = false; 

      foreach (var kvp in objectInstance) 
      { 
       if (kvp.Key.Equals(this._TargetValue)) 
       { 
        foreach (var field in kvp.Value) 
        { 
         var fieldValue = validationContext.ObjectType.GetProperty(field).GetValue(validationContext.ObjectInstance, null); 

         if (fieldValue != null && field.Equals(validationContext.MemberName)) 
         { 
          isRequiredSatisfied = true; 
          break; 
         } 
        } 
       } 
      } 

      if (isRequiredSatisfied) 
      { 
       return ValidationResult.Success; 
      } 
      else 
      { 
       return new ValidationResult(string.Empty); 
      } 
     } 
     else 
     { 
      // Must be ignored 
      return ValidationResult.Success; 
     } 
    } 
} 

我所试图实现与那就是我想要有条件验证基于在模型中的属性上。它还需要足够通用以在多个模型上重复使用。当一个指定的属性具有特定的值(我在属性中指定)时,自定义所需的验证需要匹配这些值。例如,在这个模型中:

public class PaymentModel 
{ 
    public Dictionary<object, string[]> _Mappings 
    { 
     get 
     { 
      var mappings = new Dictionary<object, string[]>(); 

      mappings.Add(true, new string[] { "StockID" }); 
      mappings.Add(false, new string[] { "Amount" }); 

      return mappings; 
     } 
    } 

    [Required] 
    public bool IsBooking { get; set; } 

    [RequiredIfAttribute("IsBooking", false)] 
    public decimal? Amount { get; set; } 

    [RequiredIf("IsBooking", true)] 
    public int? StockID { get; set; } 

    public PaymentModel() 
    { 

    } 
} 

如果IsBooking属性为true,那么我想StockId被必需的,但如果是false,然后Amount应符合规定。

目前的解决方案,我有工作,但它有2个问题:

  1. 上有_Mappings财产,我想不会有依赖性。有谁知道我会如何绕过我的方式?
  2. 如果我必须使用_Mappings属性,是否有任何方法可以将它用作private访问修饰符?目前我只能使我的解决方案工作,如果_Mappingspublic,因为validationContext.ObjectType.GetProperty("_Mappings")找不到private修饰符。 (如果我想在Web API响应中将此模型序列化为JSON,那么理想情况下我不希望将验证映射发送出去。)

回答

1

您不必使用_Mappings属性,下面的代码检查,如果相关的属性具有您的属性指定什么,如果有一个匹配,那么它会检查,看看是否相匹配的值您正在验证的属性有一个值。

protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
{ 
    var propertyTestedInfo = validationContext.ObjectType.GetProperty(this._DependentProperty); 

    if (propertyTestedInfo == null) 
    { 
     return new ValidationResult(string.Format("{0} needs to be exist in this object.", this._DependentProperty)); 
    } 

    var dependendValue = propertyTestedInfo.GetValue(validationContext.ObjectInstance, null); 

    if (dependendValue == null) 
    { 
     return new ValidationResult(string.Format("{0} needs to be populated.", this._DependentProperty)); 
    } 

    if (dependendValue.Equals(this._TargetValue)) 
    { 
     var fieldValue = validationContext.ObjectType.GetProperty(validationContext.MemberName).GetValue(validationContext.ObjectInstance, null); 


     if (fieldValue != null) 
     { 
      return ValidationResult.Success; 
     } 
     else 
     { 
      return new ValidationResult(string.Format("{0} cannot be null", validationContext.MemberName)); 
     } 
    } 
    else 
    { 
     // Must be ignored 
     return ValidationResult.Success; 
    } 
} 
+0

谢谢马丁。你是完全正确的,我试图把它复杂化。 – Ebbs

1

您可以使_Mappings为私有。更改此:

var propertyTestedInfo = validationContext.ObjectType.GetProperty("_Mappings"); 

var propertyTestedInfo = validationContext.ObjectType.GetProperty(
    "_Mappings", 
    BindingFlags.NonPublic | BindingFlags.Instance); 

要使用此overload获得的私有财产。

public PropertyInfo Type.GetProperty(string name, BindingFlags bindingAttr) 

另外,我觉得你可以不_Mappings做到完全,如果我从你当前的代码正确地计算出你的意图(并假设你在当前_Mappings字典的意思是“StockID”它说:“ReleasedStockID” )。

public class RequiredIfAttribute : RequiredAttribute 
{ 
    private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; 
    private string _DependentProperty; 
    private object _TargetValue; 

    public RequiredIfAttribute(string dependentProperty, object targetValue) 
    { 
     this._DependentProperty = dependentProperty; 
     this._TargetValue = targetValue; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     // Property info for the specified dependent property. 
     var propertyTestedInfo = validationContext.ObjectType.GetProperty(this._DependentProperty, Flags); 
     if (propertyTestedInfo == null) 
      return new ValidationResult(string.Format("{0} needs to be exist in this object.", this._DependentProperty)); 

     // And its value 
     var dependendValue = propertyTestedInfo.GetValue(validationContext.ObjectInstance, null); 
     if (dependendValue == null) 
      return new ValidationResult(string.Format("{0} needs to be populated.", this._DependentProperty)); 

     // If it meets the specified "If" predicate value 
     if (dependendValue.Equals(this._TargetValue)) 
     { 
      // Get the property being validated. 
      var validatedProperty = validationContext.ObjectType.GetProperty(validationContext.MemberName, Flags); 
      if (validatedProperty != null) 
      { 
       // Debug sanity check 
       AssertHasThisAttribute(validatedProperty); 

       // Get the property's value. 
       var validatedPropertyValue = validatedProperty.GetValue(validationContext.ObjectInstance, null); 

       // And check that is is not null 
       if (validatedPropertyValue != null) 
        return ValidationResult.Success; 
      } 
      // validation failed. 
      return new ValidationResult(string.Empty); 
     } 

     // Must be ignored 
     return ValidationResult.Success; 
    } 

    // Debug only sanity check. 
    [Conditional("DEBUG")] 
    private void AssertHasThisAttribute(PropertyInfo prop) 
    { 
     var attr = prop.GetCustomAttributes<RequiredIfAttribute>().FirstOrDefault(); 
     Debug.Assert(attr != null); 
     Debug.Assert(attr._TargetValue == _TargetValue); 
     Debug.Assert(attr._DependentProperty == _DependentProperty); 
    } 
} 
+0

嗨亚历克斯。是的,ReleasedStockID需要是StockID。我试图清理代码,但一定是错过了,因为我写这个问题时有点晚。我会更新我的例子。您建议_Mappings私人工作,谢谢。 – Ebbs