Working with VAB in Presentation Model pattern

Topics: Validation Application Block
Dec 1, 2010 at 4:22 AM

Hi,

I have been developing my WinForm app following a strict Presentation Model pattern. While everything was going pretty smooth, my devs were disturbed by a typical and boring job, that is to expose all the properties of the business model (or domain model) on the Presentation Model class once more. Even more disturbing was the fact that while doing an WinForms integrated validation, they had to re-write all the validation attributes on the presentation model properties again, since the view (Form) was bound to the Presentation Model object and integrated validation works with the bound object only. This often gave way to errors when validator attributes of presentation model and business model went out of sync, the devs just forgot to copy the validator attribute to the presentation model.

I was pretty concerned and tried to find a routing mechanism of validation from presentation model to business model. I am not sure if I have re-invented the wheel, if EntLib already had this type of functionality and may be I just missed it, but I found my small piece of code doing the trick pretty well. So here it is for all who work with presentation model pattern and if this is a re-invention, please don't blame me, I just didn't have the time to go through all the VAB docs once again, I last read them in 4.1.

1. Open the Enterprise Library solution from the EntLib source code folder.

2. Expand the 'Validation Application Block' section and go to the 'Integration' section.

3. Create a new class as (for property Attribute) as below. Name the file as 'VABPropertyValidationRouterAttribute.cs'

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Validation.Properties;
using System.Reflection;
using System.ComponentModel;
using System.Globalization;

namespace Microsoft.Practices.EnterpriseLibrary.Validation.Integration
{
    /// <summary>
    /// Provides information about the actual type that implements the validation.
    /// </summary>
    /// <remarks>
    /// Allows presentation model properties to route validations to their respective business models.
    /// </remarks>
    [global::System.AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
    public sealed class VABPropertyValidationRouterAttribute : Attribute
    {
        // fields
        readonly Type _validatedType;
        readonly string _validatedProperty;

        /// <summary>
        /// Creates a new instance of VABPropertyValidationRouter.
        /// </summary>
        /// <param name="validatedType">The actual type which contains the validation.</param>
        /// <param name="validatedProperty">The property name on the type which is to be used for validation.</param>
        public VABPropertyValidationRouterAttribute(Type validatedType, string validatedProperty)
        {
            _validatedType = validatedType;
            _validatedProperty = validatedProperty;
        }
        /// <summary>
        /// The actual type which contains the validation.
        /// </summary>
        public Type ValidatedType
        {
            get { return _validatedType; }
        }
        /// <summary>
        /// The property name on the type which is to be used for validation.
        /// </summary>
        public string ValidatedProperty
        {
            get { return _validatedProperty; }
        }
    }
}

4. Open the ValidationIntegrationHelper class and go to the GetValidator method. Change the code to as below. This will allow the ValidationProvider component to fetch the validator from the business model instead of the presentation model.

            // Raja 30.11.2010
            // In case validation routing is defined on bound object, obtain Type and Property information 
            // from routing attribute
            VABPropertyValidationRouterAttribute attrib  = (VABPropertyValidationRouterAttribute)Attribute.GetCustomAttribute(validatedProperty, typeof(VABPropertyValidationRouterAttribute));
            if (attrib != null)
            {
                // Obtain routing information
                Type routedType = attrib.ValidatedType;
                PropertyInfo routedPropertyInfo = attrib.ValidatedType.GetProperty(attrib.ValidatedProperty, BindingFlags.Public | BindingFlags.Instance);
                if (routedPropertyInfo == null)
                    throw new InvalidOperationException("Specified routed property '" + routedPropertyInfo.Name + "' not found on routed type.");
                
                // Return the validator from the routed type and property
                return PropertyValidationFactory.GetPropertyValidator(routedType,
                    routedPropertyInfo,
                    integrationProxy.Ruleset,
                    integrationProxy.SpecificationSource,
                    integrationProxy.GetMemberValueAccessBuilder());
            }
            else
            {
                return PropertyValidationFactory.GetPropertyValidator(validatedType,
                    validatedProperty,
                    integrationProxy.Ruleset,
                    integrationProxy.SpecificationSource,
                    integrationProxy.GetMemberValueAccessBuilder());
            }

 5. Open your presentation model class and go to the property which needs validation routing to business model and decorate the property as below

        [VABPropertyValidationRouter(typeof(AppUserModel), "Name")]
        public String Name
        {
            get { return _user.Name; }
            set { _user.Name = value; }
        }

6. Proceed with integrating this presentation model with your winforms as described in EntLib docs. When property validation is invoked by validation integration, the validator is obtained from the business model and not the presentation model.

 

Hope this helps you. Drop me a mail at rajabasuroy@hotmail.com for any further clarification, if you just like it or may be want to thrash it !!

Rajarshi.


Dec 1, 2010 at 8:28 AM

Doesn't it just work to decorate your model class with the System.DataAnnotations.MetadataTypeAttribute? In that case, when validating a property on the type, VAB will look at the MetadataType. The catch is that names of the properties of your model and domain should stay in sync.

[MetadataType(typeof(DomainPerson))]
public class ModelPerson
{
    public string Name { get; set; }
}

public class DomainPerson
{
    [NotNullValidator]
    [StringLengthValidator(1, 100)]
    public string Name { get; set; }
}