我只想添加条件必需属性,它与WEB API
一起使用示例
public sealed class EmployeeModel
{
[Required]
public int CategoryId{ get; set; }
public string Email{ get; set; } // If CategoryId == 1 then it is required
}
我通过(ActionFilterAttribute)使用模型状态验证
您可以实现自己的
ValidationAttribute
。也许是这样的:
public class RequireWhenCategoryAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var employee = (EmployeeModel) validationContext.ObjectInstance;
if (employee.CategoryId == 1)
return ValidationResult.Success;
var emailStr = value as string;
return string.IsNullOrWhiteSpace(emailStr)
? new ValidationResult("Value is required.")
: ValidationResult.Success;
}
}
public sealed class EmployeeModel
{
[Required]
public int CategoryId { get; set; }
[RequireWhenCategory]
public string Email { get; set; } // If CategoryId == 1 then it is required
}
这只是一个示例。它可能存在铸造问题,我不确定这是解决此问题的最佳方法。
这是我的 2 美分。它会给你一个很好的消息,比如“当前的AssigneeType值Salesman需要AssigneeId”它也适用于枚举。
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class RequiredForAnyAttribute : ValidationAttribute
{
/// <summary>
/// Values of the <see cref="PropertyName"/> that will trigger the validation
/// </summary>
public string[] Values { get; set; }
/// <summary>
/// Independent property name
/// </summary>
public string PropertyName { get; set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var model = validationContext.ObjectInstance;
if (model == null || Values == null)
{
return ValidationResult.Success;
}
var currentValue = model.GetType().GetProperty(PropertyName)?.GetValue(model, null)?.ToString();
if (Values.Contains(currentValue) && value == null)
{
var propertyInfo = validationContext.ObjectType.GetProperty(validationContext.MemberName);
return new ValidationResult($"{propertyInfo.Name} is required for the current {PropertyName} value {currentValue}");
}
return ValidationResult.Success;
}
}
像这样使用它
public class SaveModel {
[Required]
public AssigneeType? AssigneeType { get; set; }
[RequiredForAny(Values = new[] { nameof(AssigneeType.Salesman) }, PropertyName = nameof(AssigneeType))]
public Guid? AssigneeId { get; set; }
}
需要创建一个将进行验证的自定义验证类
/// <summary>
/// Provides conditional validation based on related
property value.
/// </summary>
[AttributeUsage(AttributeTargets.Property,
AllowMultiple = true)]
public class RequiredIfAttribute : ValidationAttribute, IRequired
{
#region Properties
/// <summary>
/// Gets or sets a value indicating whether other property's value should match or differ from provided other property's value (default is <c>false</c>).
/// </summary>
/// <value>
/// <c>true</c> if other property's value validation should be inverted; otherwise, <c>false</c>.
/// </value>
/// <remarks>
/// How this works
/// - true: validated property is required when other property doesn't equal provided value
/// - false: validated property is required when other property matches provided value
/// </remarks>
public bool IsInverted { get; set; }
/// <summary>
/// Gets or sets the other property name that will be used during validation.
/// </summary>
/// <value>
/// The other property name.
/// </value>
public string OtherProperty { get; private set; }
/// <summary>
/// Gets or sets the display name of the other property.
/// </summary>
/// <value>
/// The display name of the other property.
/// </value>
public string? OtherPropertyDisplayName { get; set; }
/// <summary>
/// Gets or sets the other property value that will be relevant for validation.
/// </summary>
/// <value>
/// The other property value.
/// </value>
public object? OtherPropertyValue { get; private set; }
/// <summary>
/// Gets or sets other properties that should be observed for change during validation
/// </summary>
/// <value>
/// Other properties separated by commas (CSV)
/// </value>
public string? PingPropertiesOnChange { get; set; }
/// <summary>
/// Gets a value that indicates whether the attribute requires validation context.
/// </summary>
/// <returns><c>true</c> if the attribute requires validation context; otherwise, <c>false</c>.</returns>
public override bool RequiresValidationContext => true;
#endregion Properties
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="RequiredIfAttribute"/> class.
/// </summary>
/// <param name="otherProperty">The other property.</param>
/// <param name="equalsValue">Equals this value.</param>
public RequiredIfAttribute(string otherProperty, object? equalsValue) : base("'{0}' is required because '{1}' has a value {3}'{2}'.")
{
OtherProperty = otherProperty;
OtherPropertyValue = equalsValue;
IsInverted = false;
}
#endregion Constructor
/// <summary>
/// Applies formatting to an error message, based on the data field where the error occurred.
/// </summary>
/// <param name="name">The name to include in the formatted message.</param>
/// <returns>
/// An instance of the formatted error message.
/// </returns>
public override string FormatErrorMessage(string name)
{
return string.Format(
CultureInfo.CurrentCulture,
ErrorMessageString,
name,
OtherPropertyDisplayName ?? OtherProperty,
OtherPropertyValue,
IsInverted ? "other than " : "of ");
}
public virtual bool IsRequired(object? target)
{
var otherProperty = target?.GetType()?.GetProperty(OtherProperty);
if (otherProperty == null)
return false;
object? otherValue = otherProperty?.GetValue(target);
// check if this value is actually required and validate it
return (!IsInverted && Equals(otherValue, OtherPropertyValue)) || (IsInverted && !Equals(otherValue, OtherPropertyValue));
}
/// <summary>
/// Validates the specified value with respect to the current validation attribute.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="validationContext">The context information about the validation operation.</param>
/// <returns>
/// An instance of the <see cref="T:System.ComponentModel.DataAnnotations.ValidationResult" /> class.
/// </returns>
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
ArgumentNullException.ThrowIfNull(validationContext);
var otherProperty = validationContext.ObjectType.GetProperty(OtherProperty);
if (otherProperty == null)
return new ValidationResult($"Validation: Could not find a property named '{OtherProperty}'.");
object? otherValue = otherProperty.GetValue(validationContext.ObjectInstance);
if (otherValue is null && OtherPropertyValue is null)
return ValidationResult.Success;
if ((otherValue is null && OtherPropertyValue is not null))
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
// check if this value is actually required and validate it
if ((!IsInverted && Equals(otherValue, OtherPropertyValue)) || (IsInverted && !Equals(otherValue, OtherPropertyValue)))
{
if (value == null)
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
// additional check for strings so they're not empty
if (value is string val && string.IsNullOrWhiteSpace(val))
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return ValidationResult.Success;
}
}
// It can be used as follows:
public sealed class TfaSmsOption
{
[RequiredIf(nameof(IsEnabled), true, ErrorMessage = "Phone number is required")]
[PhoneNumber(ErrorMessage = "Phone number is invalid")]
[StringLength(20, ErrorMessage = "Phone number must be less than 20 digits")]
public string PhoneNumber { get; set; } = default!;
public bool IsEnabled { get; set; }
}