如何为实现不同属性的子类提供验证?

时间:2012-09-07 19:20:36

标签: c# validation inotifypropertychanged idataerrorinfo

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

2 个答案:

答案 0 :(得分:0)

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

在琐碎的情况下,你可能能够使它发挥作用。一旦你进入更复杂的类型,我不知道你通过在父代而不是孩子中进行验证来节省很多精力。

琐碎的案例将是多个孩子拥有相似类型的属性的地方。您可以以相同的方式强制调用属性,然后您可以在父属中编写一个验证规则,该规则触发属性的名称。但是,您可能会争辩说这些属性应该是父级的一部分并由子级继承。

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

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

答案 1 :(得分: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)
  6. 完成前5个步骤后,这意味着您可以对要验证的属性进行属性化,并让验证者从类中选择要使用的验证规则(规则或字典或规则列表,以完全由您选择的属性执行) )。

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

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