0

我有一个实现 INotifyPropertyChanged 的​​父类,并且父类有多个子类。子项具有不同的属性,它们都调用 PropertyChanged。我想添加验证,但我真的不想为每个子类编写验证。验证规则是从数据库中提供的,所以我最终必须为每个孩子提取验证规则,然后根据规则检查值。如果我这样做,我认为它会有太多冗余代码,我希望将它放在父级别,因为 PropertyChanged 触发值本身的字符串值。

是否可以在父类上使用验证方法,这样我就不必为每个子类编写验证方法?请注意,每个子类中的属性都不同。

以下是我目前拥有的,在子类中进行了验证。

public Parent : INotifyChanged {
    /// <summary>
    /// Occurs when a property is changed
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raises the <see cref="PropertyChanged"/> for a given 
    /// property.
    /// </summary>
    /// <param name="propertyName"></param>
    protected void OnPropertyChanged(String propertyName) {
        // Get the hanlder
        PropertyChangedEventHandler handler = this.PropertyChanged;

        // Check that the event handler is not null
        if(null != handler) {
            // Fire the event
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

儿童1类:

public Child1 : Parent, IDataErrorInfo {
private Dictionary<string, string> m_validationErrors = new Dictionary<string, string>();

    private void Validate() {
        this.RemoveError("Child1Description");
        if(!Regex.IsMatch(Child1Description, "^([a-zA-Z '-]+)$") && !String.IsNullOrWhiteSpace(Description)) {
            this.AddError("Child1Description", "Only non-numerics allowed.");
        }
    }

    private void AddError(string columnName, string msg) {
        if(!m_validationErrors.ContainsKey(columnName)) {
            m_validationErrors.Add(columnName, msg);
        }
    }

    private void RemoveError(string columnName) {
        if(m_validationErrors.ContainsKey(columnName)) {
            m_validationErrors.Remove(columnName);
        }
    }

    public string Error {
        get {
            if(m_validationErrors.Count > 0) {
                return "Field data is invalid.";
            }
            else return null;
        }
    }

    public string this[string columnName] {
        get {
            if(m_validationErrors.ContainsKey(columnName)) {
                return m_validationErrors[columnName];
            }
            else {
                return null;
            }
        }
    }
    /// <summary>
    /// Description of the air entity
    /// </summary>
    public string Child1Description {
        get {
            return Child1description;
        }
        set {
            description = value;
            Validate();
            OnPropertyChanged("Child1Description");
        }
    }
}

Child2 类:

public Child2 : Parent, IDataErrorInfo {
private Dictionary<string, string> m_validationErrors = new Dictionary<string, string>();

    private void Validate() {
        this.RemoveError("Child2Description");
        if(!Regex.IsMatch(Child2Description, "^([a-zA-Z '-]+)$") && !String.IsNullOrWhiteSpace(Description)) {
            this.AddError("Child2Description", "Only non-numerics allowed.");
        }
    }

    private void AddError(string columnName, string msg) {
        if(!m_validationErrors.ContainsKey(columnName)) {
            m_validationErrors.Add(columnName, msg);
        }
    }

    private void RemoveError(string columnName) {
        if(m_validationErrors.ContainsKey(columnName)) {
            m_validationErrors.Remove(columnName);
        }
    }

    public string Error {
        get {
            if(m_validationErrors.Count > 0) {
                return "Field data is invalid.";
            }
            else return null;
        }
    }

    public string this[string columnName] {
        get {
            if(m_validationErrors.ContainsKey(columnName)) {
                return m_validationErrors[columnName];
            }
            else {
                return null;
            }
        }
    }
    /// <summary>
    /// Description of the air entity
    /// </summary>
    public string Child2Description {
        get {
            return Child2description;
        }
        set {
            description = value;
            Validate();
            OnPropertyChanged("Child2Description");
        }
    }
}
4

3 回答 3

0

我不相信你能做你想做的事。

在微不足道的情况下,您也许可以使其工作。一旦您进入更复杂的类型,我不知道您是否通过在父项而不是子项中进行验证来为自己节省很多精力。

最简单的情况是多个孩子具有相似类型的属性。您可以强制以相同的方式调用属性,然后您可以在父级中编写一个根据属性名称触发的验证规则。但是,您可以争辩说这些属性应该是父级的一部分并由子级继承。

更复杂的情况是每个孩子的个人属性与其他孩子的属性几乎没有相似之处。无论您将验证码放在孩子还是父母身上都没有区别。您必须为要验证的每个单独的属性编写验证代码。

鉴于您的验证规则将存储在数据库中,您可以在父级中编写一个方法,允许子级检索其属性的验证规则。孩子仍然会验证自己的属性,但您将拥有访问规则的通用代码。

于 2012-09-07T20:18:57.217 回答
0

实际上它是可以做到的,只是不是你认为你希望它发生的方式。以下将是我将遵循的步骤来做类似的事情。

  1. 获取 Microsoft 初学者企业库,因为您将使用该Microsoft.Practices.EnterpriseLibrary.Validation参考。
  2. 创建一个继承自的验证类Validator<T>(这是企业库的一部分)。
  3. 覆盖该方法DoValidate(T objectToValidate, object currentTarget, string key, ValidationResults validationResults),currentTarget 是正在验证的对象。您可以从当前目标中提取验证规则。
  4. 然后,您为该验证创建属性,使其继承自ValueValidatorAttribute.
  5. 您覆盖DoCreateValidator(Type targetType, Type ownerType, MemberValueAccessBuilder memberValueAccessBuilder, ValidatorFactory validatorFactory)属性类中的方法。

完成前 5 个步骤后,这意味着您可以为要验证的属性赋予属性,并让验证器从类中选择要使用的验证规则(规则列表或字典或要对属性执行的规则完全由您选择)。

下一步是将接口移动IDataErrorinfo到父类,创建一个Validate()从调用中获取结果的方法,如果Microsoft.Practices.EnterpriseLibrary.Validation.Validation.Validate(this);发生错误,则返回验证错误。

由您决定如何放置方法调用。测试时最简单的方法是将其放在OnPropertyChanged您拥有的方法中。

于 2012-09-09T19:22:15.690 回答
0

我最终传递了属性名称、属性值和我想用来验证的规则列表。将 Validate 方法存储在父级中,因此无论哪个子级使用它都会运行,它将使用自己的规则,但保持相同的验证错误消息字典。

 protected void Validate(string propertyName, string propertyValue, List<ValidRule> validRules) {
      string temp = propertyValue.ToString();
      this.RemoveError(propertyName);
      if(propertyName.Equals("Description")) {
           foreach(ValidRule validRule in validRules) {
                if(!Regex.IsMatch(propertyValue, validRule.Rule) && !String.IsNullOrWhiteSpace(propertyValue)) {
                     this.AddError(propertyName, validRule.ErrorMessage);
                     break;
                }
           }
      }
 }
于 2012-09-20T14:38:20.513 回答