【问题标题】:How to provide validation for child classes that implement different properties?如何为实现不同属性的子类提供验证?
【发布时间】:2012-09-07 19:20:36
【问题描述】:

我有一个实现 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));
        }
    }
}

Child1 班级:

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");
        }
    }
}

【问题讨论】:

  • 问:是否可以在父类上有验证方法?答:当然。为什么不?你的问题到底是什么?能贴出代码吗?
  • @paulsm4 抱歉,正在尝试清理它。添加了代码。

标签: c# validation inotifypropertychanged idataerrorinfo


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

    我最终传递了属性名称、属性值和我想用来验证的规则列表。将 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;
                    }
               }
          }
     }
    

    【讨论】:

      【解决方案3】:

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

      1. 获取适用于初学者的 Microsoft 企业库,因为您将使用 Microsoft.Practices.EnterpriseLibrary.Validation 参考。
      2. 创建一个继承自 Validator&lt;T&gt; 的验证类(这是 Enterprise 库的一部分)。
      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 方法中。

      【讨论】:

        猜你喜欢
        • 2017-04-08
        • 1970-01-01
        • 2011-06-25
        • 2018-08-02
        • 2011-01-04
        • 2014-03-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-27
        相关资源
        最近更新 更多