使用 C# 和 Web API 的自定义必需属性,并将私有访问修饰符与验证上下文一起使用
Custom required attribute with C# & Web API and using private access modifier with validation context
我有以下自定义必需属性:
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
应该是必需的。
目前我的解决方案有效,但有两个问题:
- 存在对
_Mappings
属性 的依赖,我不希望有。有谁知道我将如何解决我的问题?
- 如果我必须按原样使用
_Mappings
属性,有没有办法将它用作 private
访问修饰符?目前我只能在 _Mappings
为 public
时使我的解决方案有效,因为 validationContext.ObjectType.GetProperty("_Mappings")
找不到 private
修饰符。 (如果我想在 Web API 响应中将该模型序列化为 JSON,那么我最好不要发送我的验证映射。)
您可以将 _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)
此外,如果我从您当前的代码中正确地理解了您的意图(并假设您的意思是 "StockID",它说 "ReleasedStockID" 在当前 _Mappings
词典中)。
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);
}
}
您不必使用 _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;
}
}
我有以下自定义必需属性:
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
应该是必需的。
目前我的解决方案有效,但有两个问题:
- 存在对
_Mappings
属性 的依赖,我不希望有。有谁知道我将如何解决我的问题? - 如果我必须按原样使用
_Mappings
属性,有没有办法将它用作private
访问修饰符?目前我只能在_Mappings
为public
时使我的解决方案有效,因为validationContext.ObjectType.GetProperty("_Mappings")
找不到private
修饰符。 (如果我想在 Web API 响应中将该模型序列化为 JSON,那么我最好不要发送我的验证映射。)
您可以将 _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)
此外,如果我从您当前的代码中正确地理解了您的意图(并假设您的意思是 "StockID",它说 "ReleasedStockID" 在当前 _Mappings
词典中)。
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);
}
}
您不必使用 _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;
}
}