Change validation on child class

Topics: Validation Application Block
Mar 12, 2007 at 10:58 PM
Edited Mar 12, 2007 at 10:59 PM
Suppose you have two classes Customer and PreferredCustomer such as the classes in the example :o)

In the sample they show this:

public class Customer
{
CustomerNameValidator
public string Name { get { /* ... */ } set{ /* ... */ } }

DiscountValidator
public virtual double Discount { get { /* ... */ } set{ /* ... */ } }
}

public class PreferredCustomer : Customer
{
PreferredDiscountValidator
public virtual double Discount { get { /* ... */ } set{ /* ... */ } }
}

It then states that if inherited members are overriden, that base validators will not apply, if members are not overriden then base validators will apply. So CustomerNameValidator applies whereas DiscountValidator does not on an object of type PreferredCustomer

However, there are a few issues:
1) What if you do not want to change the get nor set, just the validation - especially if there is complex logic in the setter or getter - it seems that forcing an override is not practical - and may be impossible if there is say a side effect to a field that is read only in the child class.
2) What if you want to keep the existing validation (i.e. it still applies), but add to it (i.e. child class is more restrictive). Say a regular customer has an optional field "email", which has validators like

IgnoreNulls
OurCustomEmailValidator

How would we keep the existing validation, but just add that it is now a required field?


So forcing an override just to change the validation does not seem feasible. Is there another way to change the validation on a child object?

Thanks
Mar 12, 2007 at 11:02 PM
PS - one possibility we came up with (although we are not sure we like it) is that you could add a self validation function to the child class, which then checks the additional restrictions...
Mar 21, 2007 at 5:49 PM
Hi,

I assume that CustomerNameValidator, DiscountValidator etc, which you have listed above are Custom Validators.
Can you send me some articles/links where I can look up an example of an implementation for Custom Validation.

Thanks a lot.
Mar 21, 2007 at 8:37 PM
I would look at using RuleSets. Here is an example I came up with quickly ( really quickly :) that uses similar inheritance and RuleSets to keep the same property but change the ruleset for validation:

namespace RuleSets
{
    public class Customer
    {
        private string _ruleSet;
 
        public Customer() : this("Customer") {}
 
        public Customer(string ruleSet)
        {
            _ruleSet = ruleSet;
        }
 
        private string _name;
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
 
        private int _discount;
 
        [RangeValidator(1, RangeBoundaryType.Inclusive, 10,RangeBoundaryType.Inclusive,
						Ruleset = "Customer", MessageTemplate = "Invalid Custsomer Discount.")]
        [RangeValidator(1, RangeBoundaryType.Inclusive, 50, RangeBoundaryType.Inclusive,
						Ruleset = "PreferredCustomer", MessageTemplate = "Invalid Preferred Customer Discount.")]
        public virtual int PercentDiscount
        {
            get { return _discount; }
            set { _discount = value; }
        }
 
        public virtual ValidationResults Validate()
        {
            return Validation.Validate(this, _ruleSet);
        }
    }
 
    public class PreferredCustomer : Customer
    {
        public PreferredCustomer() : base("PreferredCustomer") {}
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            List<Customer> customers = new List<Customer>();
 
            Customer customer = new Customer();
            customer.PercentDiscount = 5;
 
            Customer preferredCustomer = new PreferredCustomer();
            preferredCustomer.PercentDiscount = 60;
 
            customers.Add(customer);
            customers.Add(preferredCustomer);
 
            foreach (Customer cust in customers)
            {
                ValidationResults results = cust.Validate();
 
                if (!results.IsValid)
                {
                    foreach (ValidationResult result in results)
                    {
                        Console.WriteLine("Error: {0}", result.Message);
                    }
                    Console.ReadLine();
                }
            }
        }
    }
}

Regards,

Dave

______________________

David Hayden
Microsoft MVP C#
Mar 29, 2007 at 6:52 PM
No rulessets are not reasonable. You are now saying that when writing a base class you have to know about and define rules for any child classes you may later write. That is not reasonable.
Mar 29, 2007 at 9:03 PM
I am not saying that at all. I am just brainstorming options because you don't like:

1) Overriding a virtual member
2) SelfValidation
3) RuleSets

Personally, I would just override the member because that is what inheritance is all about.

Your reason of possibly having complex logic in a getter or setter is not a good practice. The expectation of a client is that a get or set of a property is inexpensive and is really just interrogating a private member. If you are doing complex logic in a getter or setter, you probably should switch to a method, but more importantly, remove the complex logic from the property and into a private method or probably a separate class where it can be properly unit tested. Once the complex logic has been removed directly from the getter / setter overriding is a moot point.

There are plenty of design patterns, like a simple Template Method, that you can use to refactor yourself to what may be a better design.

Hopefully one of the options will work for you. There are probably other solutions but it is difficult to brainstorm knowing nothing about your domain model.

Regards,

Dave

______________________

David Hayden
Microsoft MVP C#