Fluent Configuration API for Validation Application Block

Topics: Validation Application Block
Aug 6, 2010 at 2:04 PM

The Validation Application Block v5 contains a nice fluent configuration API. However, I'm not able to find the fluent API for the Validation Application Block.

Is there a fluent API available for VAB available?

 

Aug 6, 2010 at 10:41 PM

There is not. We considered it unnecessary since you can just new up validator objects directly.

 

Aug 22, 2010 at 12:35 PM
Edited Aug 22, 2010 at 7:59 PM

Thank you for your response. I agree that a fluent configuration API for the Validation Application Block is not strictly necessary, but I'm having trouble implementing a few basic use cases while doing code based configuration with the VAB. These are:

  1. Use code-based configuration with only using framework supplied validations.
  2. Use code-based configuration with some custom validators.
  3. Use code-based configuration in a way that the configuration is type safe and the validated domain classes can easily be refactored without breaking the configuration.

I think you agree that these are basic use cases and should be easily achievable with the current API, because this is why you considered is unnecessary. Please also note use case 3, because having a type safe configuration is perhaps the most important feature when using code-based configuration. If we can't do this, why shouldn't we use plain old XML in the first place?

I've tried to implement these use case, but perhaps I'm doing something wrong, because I'm not able to achieve this with just a little amount of code. Here are a few examples

For use case 1 imagine a domain with single type 'Person' with two properties with each two validators, divided among two rulesets named 'Default' and 'Alternative':

 

public class ValidationSettingsConfigurationSource : IConfigurationSource
{
    private readonly ValidationSettings settings;

    public ValidationSettingsConfigurationSource(ValidationSettings settings)
    {
        this.settings = settings;
    }

    public ConfigurationSection GetSection(string sectionName)
    {
        if (ValidationSettings.SectionName == sectionName)
        {
            return this.settings;
        }

        return null;
    }

    public void Add(string sectionName, System.Configuration.ConfigurationSection configurationSection)
    {
        throw new NotImplementedException();
    }

    public void AddSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
    {
        throw new NotImplementedException();
    }

    public void Remove(string sectionName)
    {
        throw new NotImplementedException();
    }

    public void RemoveSectionChangeHandler(string sectionName, ConfigurationChangedEventHandler handler)
    {
        throw new NotImplementedException();
    }

    public event EventHandler<ConfigurationSourceChangedEventArgs> SourceChanged;

    public void Dispose()
    {
    }
}


    const string DefaultRuleset = "Default";
    const string AlternativeRuleset = "Alternative";

    var settings = new ValidationSettings()
    {
        Types =
        {
            new ValidatedTypeReference(typeof(Person))
            {
                DefaultRuleset = DefaultRuleset,
                Rulesets =
                {
                    new ValidationRulesetData(DefaultRuleset)
                    {
                        Properties =
                        {
                            new ValidatedPropertyReference("FirstName")
                            {
                                Validators =
                                {
                                    new NotNullValidatorData("FirstName_NotNull"),
                                    new StringLengthValidatorData("FirstName_Smaller100")
                                    {
                                        LowerBound = 1,
                                        LowerBoundType = RangeBoundaryType.Inclusive,
                                        UpperBound = 100,
                                        UpperBoundType = RangeBoundaryType.Inclusive
                                    }
                                }
                            },
                            new ValidatedPropertyReference("LastName")
                            {
                                Validators =
                                {
                                    new NotNullValidatorData("LastName_NotNulol"),
                                }
                            },
                        },
                    },
                    new ValidationRulesetData(AlternativeRuleset)
                    {
                        Properties =
                        {
                            new ValidatedPropertyReference("FirstName")
                            {
                                Validators =
                                {
                                    new StringLengthValidatorData("FirstName_Smaller10")
                                    {
                                        LowerBound = 1,
                                        LowerBoundType = RangeBoundaryType.Inclusive,
                                        UpperBound = 10,
                                        UpperBoundType = RangeBoundaryType.Inclusive
                                    }
                                }
                            }                                    
                        }
                    }
                }
            },
        }
    };

    var configuration = new ValidationSettingsConfigurationSource(settings);

 

For the second use case, imagine a custom validator on the 'Person' type that simply verifies whether the first name and last name differ.

 

var settings = new ValidationSettings()
{
    Types =
    {
        new ValidatedTypeReference(typeof(Person))
        {
            DefaultRuleset = DefaultRuleset,
            Rulesets =
            {
                new ValidationRulesetData(DefaultRuleset)
                {
                    Validators =
                    {
                        new CustomValidatorData("PersonValidator", typeof(PersonValidator))
                    },
                }
            }
        }
    }
}
public class PersonValidator : Validator<Person>
{
    public PersonValidator(NameValueCollection attributes)
        : base(string.Empty, string.Empty)
    {
    }

    protected override void DoValidate(Person objectToValidate, object currentTarget, string key,
        ValidationResults validationResults)
    {
        if (objectToValidate.FirstName == objectToValidate.LastName)
        {
            this.LogValidationResult(validationResults, "First name and last name must be different.",
                currentTarget, key);
        }
    }

    protected override string DefaultMessageTemplate
    {
        get { throw new NotImplementedException(); }
    }
}

 

Is there a way to dramatically decrease the amount of code that needs to be written in the above examples?

It is even harder to achieve the third use case. In the code for the first use case you already saw the use of literals for specifying the 'FirstName' and 'LastName'. The current API doesn't allow you to easily get rid of it. I also didn't find a way to easily get rid of literals when configuring custom validators. Look for instance at the following example:

 

var settings = new ValidationSettings()
{
    Types =
    {
        new ValidatedTypeReference(typeof(Person))
        {
            DefaultRuleset = DefaultRuleset,
            Rulesets =
            {
                new ValidationRulesetData(DefaultRuleset)
                {
                    Validators =
                    {
                        new CustomValidatorData("ThingValidator", typeof(SpecialThingValidator))
                        {
                            Attributes =
                            {
                                { "maximumNumberOfUnshippedOrders", "3" },
                                { "maximumCreditCardLimit", "1004.50" },
                                { "someOtherProperty", "Some string" },
                            }
                        }
                    },
                }
            }
        }
    }
};

I like to hear from you in which ways my code samples can be improved in such way that is less code, easer to write, more readable and easier to refactor.

 

Thanks in advance.