如何在MVC中的数据注释中有条件地验证?

时间:2015-12-18 09:45:38

标签: c# asp.net-mvc

我有以下型号:

public class PasswordResetModel {

public bool PasswordCreationStatus { get; set; }

[Display(Name = "AcctNumber", ResourceType = typeof(Resources.Register))]
    [ValidateMustHaveAtLeastOne("AtLeastOneShoudlbeThere", "")]
    //Account number is numeric
    [RegularExpression(@"^[0-9]+$", ErrorMessageResourceType = typeof(Resources.Register),
      ErrorMessageResourceName = "AccountNumberRegularExpr")]
    [ScriptIgnore]
    public int? AccountNumber { get; set; }

    [Display(Name = "BirthDate", ResourceType = typeof(Resources.Register))]
    [ValidateMustHaveAtLeastOne("AtLeastOneShoudlbeThere", "")]
    [ScriptIgnore]
    public string BirthDate { get; set; }

    [Display(Name = "SSN", ResourceType = typeof(Resources.Register))]
    [ValidateMustHaveAtLeastOne("AtLeastOneShoudlbeThere", "")]
    //Accepts only four digits
    [RegularExpression(@"^\d{4}$", ErrorMessageResourceType = typeof(Resources.Register),ErrorMessageResourceName = "SSNRegularExpr")]
    [StringLength(4, ErrorMessageResourceType = typeof(Resources.Register),ErrorMessageResourceName = "SSNRegularExpr")]
    [ScriptIgnore]
    public string SSN { get; set; }
}

我想确保至少输入3(帐号,SSN和BirthDate)中的任何一个。下面是ValidateMustHaveAtleastOne类。但是我想仅在PasswordCreationStatus为假时才进行验证。

ValidateMustHaveAtLeastOne类如下所示:

[AttributeUsage(AttributeTargets.Property)]
  public class ValidateMustHaveAtLeastOne : ValidationAttribute, IClientValidatable {

    #region Construnctor
    public ValidateMustHaveAtLeastOne(string groupName, string validationMessage) {
      GroupName = groupName;
      ValidationMessage = validationMessage;
    }
    #endregion

    #region Properties
    /// <summary>
    /// Name of the group of the properties
    /// </summary>
    public string GroupName { get; private set; }

    /// <summary>
    /// Vaidation message
    /// </summary>
    public string ValidationMessage { get; private set; }

    #endregion

    #region Public overrides
    /// <summary>
    /// Validates the group of properties.
    /// </summary>
    /// <param name="value">The value to validate.</param>
    /// <param name="context">The context information about the validation operation.</param>
    /// <returns>An instance of the ValidationResult class.</returns>
    protected override ValidationResult IsValid(object value, ValidationContext context) {
      foreach (var property in GetGroupProperties(context.ObjectType)) {
        var propertyValue = property.GetValue(context.ObjectInstance, null);
        if (propertyValue != null) {
          return null;
        }
      }
      return new ValidationResult(ValidationMessage);
    }
    #endregion

    #region Implementation of IClientValidateable
    /// <summary>
    /// To enable client side implementation of same validtion rule.
    /// </summary>
    /// <param name="metadata">The model metadata.</param>
    /// <param name="context">The controller context.</param>
    /// <returns>The client validation rules for this validator.</returns>

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
      var groupProperties = GetGroupProperties(metadata.ContainerType);
      List<string> propertyNames = new List<string>();
      foreach (var property in groupProperties) {
        propertyNames.Add(property.Name);
      }
      var rule = new ModelClientValidationRule {
        ErrorMessage = this.ValidationMessage
      };
      rule.ValidationType = string.Format("group", GroupName.ToLower());
      rule.ValidationParameters["propertynames"] = string.Join(",", propertyNames);
      yield return rule; // yield key word is used as return type is  declared as IEnumerable<ModelClientValidationRule>
    }
    #endregion

        /// Returns the group of properties that implements this attribute
    /// </summary>
    /// <param name="type">Type of the Model</param>
    /// <returns>List of properties</returns>
    private IEnumerable<PropertyInfo> GetGroupProperties(Type type) {
      var propertyInfo = new List<PropertyInfo>();
      foreach (PropertyInfo property in type.GetProperties()) {
        if (property.GetCustomAttributes(typeof(ValidateMustHaveAtLeastOne), false).GetLength(0) > 0) {
          propertyInfo.Add(property);
        }
      }
      return propertyInfo;
    }


  }

我认为它可能以最小的增强。 任何帮助将受到高度赞赏。 提前谢谢。

3 个答案:

答案 0 :(得分:1)

更新了您的验证,以验证依赖属性是否与目标值匹配,然后进行验证。

[AttributeUsage(AttributeTargets.Property)]
public class ValidateMustHaveAtLeastOne : ValidationAttribute, IClientValidatable {

#region Construnctor
public ValidateMustHaveAtLeastOne(string groupName, string validationMessage, string dependentProperty, object targetValue) {
  GroupName = groupName;
  ValidationMessage = validationMessage;

  DependentProperty = dependentProperty;
  TargetValue = targetValue;
}
#endregion

#region Properties
/// <summary>
/// Name of the group of the properties
/// </summary>
public string GroupName { get; private set; }

/// <summary>
/// Vaidation message
/// </summary>
public string ValidationMessage { get; private set; }

/// <summary>
/// Gets or sets the dependent property.
/// </summary>
/// <value>
/// The dependent property.
/// </value>
public string DependentProperty { get; set; }

/// <summary>
/// Gets or sets the target value.
/// </summary>
/// <value>
/// The target value.
/// </value>
public object TargetValue { get; set; }

#endregion

#region Public overrides
/// <summary>
/// Validates the group of properties.
/// </summary>
/// <param name="value">The value to validate.</param>
/// <param name="context">The context information about the validation operation.</param>
/// <returns>An instance of the ValidationResult class.</returns>
protected override ValidationResult IsValid(object value, ValidationContext context) {
    var containerType = validationContext.ObjectInstance.GetType();
    var field = containerType.GetProperty(this.DependentProperty);
    if (field != null)
    {
        // get the value of the dependent property
        var dependentvalue = field.GetValue(validationContext.ObjectInstance, null);

        // compare the value against the target value
        if ((dependentvalue == null && this.TargetValue == null)
            || (dependentvalue != null && dependentvalue.Equals(this.TargetValue)))
        {
            foreach (var property in GetGroupProperties(context.ObjectType)) {
            var propertyValue = property.GetValue(context.ObjectInstance, null);
            if (propertyValue != null) {
              return null;
                }
          }
          return new ValidationResult(ValidationMessage);
        }
    }

  return ValidationResult.Success;
}
#endregion

#region Implementation of IClientValidateable
/// <summary>
/// To enable client side implementation of same validtion rule.
/// </summary>
/// <param name="metadata">The model metadata.</param>
/// <param name="context">The controller context.</param>
/// <returns>The client validation rules for this validator.</returns>

public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
  var groupProperties = GetGroupProperties(metadata.ContainerType);
  List<string> propertyNames = new List<string>();
  foreach (var property in groupProperties) {
    propertyNames.Add(property.Name);
  }
  var rule = new ModelClientValidationRule {
    ErrorMessage = this.ValidationMessage
  };
  rule.ValidationType = string.Format("group", GroupName.ToLower());
  rule.ValidationParameters["propertynames"] = string.Join(",", propertyNames);

  string depProp = this.BuildDependentPropertyId(metadata, context as ViewContext);
  // find the value on the control we depend on; if it's a bool, format it javascript style
  string targetValue = (this.TargetValue ?? string.Empty).ToString();
  if (this.TargetValue.GetType() == typeof(bool))
  {
     targetValue = targetValue.ToLower();
  }

  rule.ValidationParameters["dependentproperty"] = depProp;
  rule.ValidationParameters["targetvalue"] = targetValue;
  yield return rule; // yield key word is used as return type is  declared as IEnumerable<ModelClientValidationRule>
}
#endregion

    /// Returns the group of properties that implements this attribute
/// </summary>
/// <param name="type">Type of the Model</param>
/// <returns>List of properties</returns>
private IEnumerable<PropertyInfo> GetGroupProperties(Type type) {
  var propertyInfo = new List<PropertyInfo>();
  foreach (PropertyInfo property in type.GetProperties()) {
    if (property.GetCustomAttributes(typeof(ValidateMustHaveAtLeastOne), false).GetLength(0) > 0) {
      propertyInfo.Add(property);
    }
  }
  return propertyInfo;
}

 private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
    string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentProperty);

    // This will have the name of the current field appended to the beginning, because the TemplateInfo's context has had this fieldname appended to it.
    var thisField = metadata.PropertyName + "_";
    if (depProp.StartsWith(thisField, StringComparison.OrdinalIgnoreCase))
    {
        depProp = depProp.Substring(thisField.Length);
    }

    return depProp;
} 

}

<强>用法

 [ValidateMustHaveAtLeastOne("AtLeastOneShoudlbeThere", "", "PasswordCreationStatus", false)]

答案 1 :(得分:0)

您可以使用已完成(已测试)的库进行某些条件的验证。

有很多这样的库,但最着名的可能是foolproof

在上面的链接中,您可以看到此用法和文档。

我认为以下验证属性适合您的情况。

[RequiredIfTrue("PasswordCreationStatus")]

答案 2 :(得分:0)

您可以添加仅在至少提供其中一个属性时才有效的计算属性:

public class PasswordResetModel 
{

    public bool PasswordCreationStatus { get; set; }

    [Display(Name = "AcctNumber", ResourceType = typeof(Resources.Register))]        
    //Account number is numeric
    [RegularExpression(@"^[0-9]+$", ErrorMessageResourceType = typeof(Resources.Register),
      ErrorMessageResourceName = "AccountNumberRegularExpr")]
    [ScriptIgnore]
    public int? AccountNumber { get; set; }

    [Display(Name = "BirthDate", ResourceType = typeof(Resources.Register))]        
    [ScriptIgnore]
    public string BirthDate { get; set; }

    [Display(Name = "SSN", ResourceType = typeof(Resources.Register))]      
    //Accepts only four digits
    [RegularExpression(@"^\d{4}$", ErrorMessageResourceType = typeof(Resources.Register),ErrorMessageResourceName = "SSNRegularExpr")]
    [StringLength(4, ErrorMessageResourceType = typeof(Resources.Register),ErrorMessageResourceName = "SSNRegularExpr")]
    [ScriptIgnore]
    public string SSN { get; set; }

    [Required(ErrorMessage = "Must Have At Least One of the properties")]
    public string MustHaveAtLeastOneIsValid {
       get{
           return  this.SSN != null || this.BirthDate != null || this.AccountNumber.HasValue ? "valid": null;
       }
    }
}