Validation and Inheritance

If you use inheritance, you need to know how the validation rules are applied throughout the class hierarchy. Here is an example of a simple hierarchy, where class PreferredCustomer inherits from class Customer. (The validator attributes used in the example, such as CustomerNameValidator refer to custom validators and are not validators included with the Validation Application Block.)

public class Customer
{
  [CustomerNameValidator]
  public string Name
  {
    get { ... } 
    set { ... }
  }

  [DiscountValidator]
  public virtual double Discount
  {
    get { ... }
    set { ... }
  }
}

public class PreferredCustomer : Customer
{
  [PreferredDiscountValidator]
  public override double Discount
  {
    get { ... }
    set { ... }
  }
}
'Usage
Public Class Customer

  <CustomerNameValidator()> _
  Public Property Name(ByVal _name As String)
    Get
      ' ...
    End Get
    Set(ByVal value)
      ' ...
    End Set
  End Property

  <DiscountValidator()> _
  Public Overridable Property Discount(ByVal _discount As Double)
    Get
      ' ...
    End Get
    Set(ByVal value)
      ' ...
    End Set
  End Property

End Class

Public Class PreferredCustomer
  Inherits Customer
  <PreferredDiscountValidator()> _
  Overrides Public Property Discount(ByVal _discount As Double)
    Get
      ' ...
    End Get
    Set(ByVal value)
      ' ...
    End Set
  End Property
End Class

In this example, the PreferredCustomer class derives from the Customer class, and it also overrides the Discount property.

There are two rules for how validators work within a class hierarchy:

  • If a derived class inherits a member and does not override it, the member's validators from the base class apply to the derived class.
  • If a derived class inherits a member but overrides it, the member's attributes from the base class do not apply to the derived class.

In this example, the CustomerNameValidator attribute applies to the PreferredCustomer class, but the DiscountValidator attribute does not. Instead, the PreferredDiscountValidator attribute applies.

If this is not the desired behavior, you can use validators of base classes to check instances of derived classes. The following code example shows how to do this. It assumes that you have resolved an instance of the Validation Application Block ValidatorFactory class and stored it in a variable named valFactory.

Validator<Customer> customerValidator = valFactory.CreateValidator<Customer>();
PreferredCustomer myPreferredCustomer = new PreferredCustomer();
// Set properties of PreferredCustomer here
ValidationResults r = customerValidator.Validate(myPreferredCustomer);
'Usage
Dim customerValidator As Validator(Of Customer) = valFactory.CreateValidator(Of Customer)()
Dim myPreferredCustomer As PreferredCustomer = New PreferredCustomer()
' Set properties of PreferredCustomer here
Dim r As ValidationResults = customerValidator.Validate(myPreferredCustomer)

This example validates a PreferredCustomer object. However, the validation is based on the attributes of the Customer base class. The validation rules defined on the PreferredCustomer class are not applied.

You can use the CreateValidator(Type) overload of the ValidatorFactory class to create a validator that is specific to a class that you provide at run time.

public ValidationResults CheckObject(object obj)
{
  if (obj != null)
  {
    Validator v = valFactory.CreateValidator(obj.GetType());
    return v.Validate(obj);
  }
  else
  {
    return null;
  }
}
'Usage
Public Function ValidationResults(ByVal obj As Object)
  If Not obj Is Nothing Then
    Dim v As Validator = valFactory.CreateValidator(obj.GetType())
    Return v.Validate(obj)
  Else
    Return Nothing
  End If
End Function

This example creates a validator based on the run time type of the input argument to the CheckObject method.