16 May 2007

Developing a Custom Property Comparison Validator using Entlib's VAP

 Here's a sample scenario where, in an application using Entlib's Validation Application Block (VAP), the validation of an entity's member depends on the value of  another property. In this case the entity I want to validate has two properties. Using the attributes approach to support validation, the entity's code looks like this:

public class MyEntity

{

    private string myVar1;

 

    [NotNullValidator()]

    [StringLengthValidator(0, 20, MessageTemplate = "MyProperty1 must be less than 20 characters length")]

    public string MyProperty1

    {

        get { return myVar1; }

        set { myVar1 = value; }

    }

 

    private string myVar2;

 

    [MyProp2Validator()]

    public string MyProperty2

    {

        get { return myVar2; }

        set { myVar2 = value; }

    }

}

The validators for Property1 are built in validators, no big deal with that. The thing here is that I want the second property to be validated against the value of the first one. Let’s say, for example, that Property2 is valid if it begins with the value of Property1.

Although the VAP ships with a PropertyComparisonValidator, it is limited to comparisons of type =, !=, >, >=, <>. So it seems that a custom validator has to be made. But how to obtain the actual value of Property1 from the Property2's custom validator?

One approach is to use reflection. Using reflection the DoValidate method of MyProperty2Validator would look something like this:

protected override void DoValidate(string objectToValidate, object currentTarget, string key, ValidationResults validationResults){

    //obtain the other property value through reflection

    PropertyInfo prop = currentTarget.GetType().GetProperty("MyProperty1");

    string myProp1Value = prop.GetValue(currentTarget, null) as string;

    //some custom logic here

    if (! objectToValidate.StartsWith( myProp1Value ){

        LogValidationResult(validationResults,"msg",currentTarget,key);

    }

}

But the dropdown of using reflection is that it wouldn't work with UI integration.

What do work with integration is what the built-in PropertyComparisonValidator does, and that is using the ValueAccess class. The ValueAccess class allow us to find other members in an integration friendly way throuhg its GetValue method. See how this is done in our Property2 valdiator:

[ConfigurationElementType(typeof(MyProp2ValidatorData))]

class MyProp2Validator : Validator<string> {

    private ValueAccess valueAccess;

    internal const string OtherPropName = "MyProperty1";

 

    public MyProp2Validator(ValueAccess valueAccess):base (Resources.MyProp2ValidatorMessageTemplate,null){

        this.valueAccess = valueAccess;

    }

 

    protected override void DoValidate(string objectToValidate, object currentTarget, string key, ValidationResults validationResults){

        object myProp1;

        string valueAccessFailureMessage;

        //try to obtain the value of property 1.

        if (!this.valueAccess.GetValue(currentTarget, out myProp1, out valueAccessFailureMessage)) {

            base.LogValidationResult(validationResults, string.Format(CultureInfo.CurrentUICulture, Resources.MyProp2ValidatorFailureToRetrieveProp1, new object[] { this.valueAccess.Key, valueAccessFailureMessage }), currentTarget, key);

        }

        else {

            //custom validation logic between prop1 and prop2

            if (!objectToValidate.StartsWith((string)myProp1)) {

                base.LogValidationResult(validationResults, string.Format(CultureInfo.CurrentUICulture, this.MessageTemplate, new object[] { objectToValidate, myProp1 }), currentTarget, key);

            }

        }

    }

 

    protected override string DefaultMessageTemplate {

        get { return Resources.MyProp2ValidatorDefaultMessageTemplate; }

    }

}

What’s left is to provide the ValueAccess to the validator’s constructor. This is done in the MyProp2ValidatorAttribute class, if using attributes to place validators, or in the MyProp2ValidatorData if using configuration files. MyProp2ValidatorAttribute would look like this:

public class MyProp2ValidatorAttribute : ValueValidatorAttribute

{

    protected override Validator DoCreateValidator(Type targetType)

    {

        throw new InvalidOperationException("A member value access builder is needed.");

    }

 

    protected override Validator DoCreateValidator(Type targetType, Type ownerType, MemberValueAccessBuilder memberValueAccessBuilder)

    {

        PropertyInfo propertyInfo = ownerType.GetProperty(MyProp2Validator.OtherPropName);

        if (propertyInfo == null)

        {

            throw new InvalidOperationException(String.Format(Resources.MyProp2ValidatorAttributeCouldNotFindProperty, new string[] { ownerType.Name, MyProp2Validator.OtherPropName }));

        }

        return new MyProp2Validator(memberValueAccessBuilder.GetPropertyValueAccess(propertyInfo));

    }

}

Finally, you can test the validation logic using the following code:

private void buttonValidate_Click(object sender, EventArgs e)

{

    MyEntity ent = new MyEntity();

    ent.MyProperty1 = textBoxProp1.Text;

    ent.MyProperty2 = textBoxProp2.Text;

    ValidationResults r = Validation.Validate<MyEntity>(ent);       

    if (!r.IsValid)

    {

        DisplayValidationResults(r);

    }

    else {

        MessageBox.Show("ok");

    }

}

It's only left to provide the UI integration for the custom validator to assure averything's working right. I hope to be posting this in a second part soon.

16 April 2007

Workflow Foundation conference's material

The material (powerpoint presentation and code samples) of the Introduction To Workflow Foundation conference is now available for download.

20 March 2007

Sign up in the WF's conference @ MS Argentina

On Thursday the 12th (from 9 am to 1 pm) I will take part in the “Introduction to Windows Workflow Foundation” conference given at Microsoft Argentina, along with Diego Gonzalez (MVP) and Jorge Fioranelli.

Here is the conference abstract:

Between the new technologies included in the recently released .Net Framework 3.0, we found Windows Workflow Foundation, which consists on the most innovating tool set included in this new framework’s version. The possibility of extracting the workflow concept from the applications as a reusable and extensible element is an extremely innovating aspect in terms of application design. Tools are also included that let you visually edit workflows, enabling analysts and users without programming knowledge to extend applications with a minimum impact in the whole application.

The conference will consist on an introduction to the technology, showing its main characteristics, a demonstration of its more relevant advanced aspects, and will end with two examples of real projects where Windows Workflow Foundation has been used in combination with other technologies to solve common issues.

You can see the details and sign up here.

See you there!

17 March 2007

Environmental Overrides

February 2007 CTP of Enterprise Library 3.0 includes a very cool feature called "Environmental Overrides". This feature provides the capability of defining different environments in your application's configuration, and overriding some of the configuration settings in these environments. This is very useful in common scenarios, such as having different connection strings in development, testing and production. For a more detail explanation of how this works, read here, directly from its creator.

The tool sounded very promising to me, but pitifully, I couldn't make it work. I found the following two issues:

  1. When I generate the merged file for one environment (by clicking the "Save merged configuration" item of its context menu), the resulting file do not have the property's value overridden. This is an important problem for me, since it prevents the whole override feature to work. I reported this in more detail here, but the P&P people couldn't reproduce the bug.
  2. After reopening the app.config file, and loading one delta file for an environment, the settings for my overridden properties do not appear as expected. I see the "Don't Override Properties" option instead. I have also reported this with no luck here.
Is anyone having the same problem? Did anyone make it work?