Using the EntLib Validation Block with WPF Part 2: Configuration Based Validation

Saturday, January 31, 2009 – 6:27 PM

In part 1 I covered using the EntLib Validation Block in combination with the Presentation Model pattern described in the p&p’s Composite Application Guidance for WPF (Prism). The end result was a presentation model decorated with attributes which describe how to validate the model and surface validation errors to the user via an implementation of the IDataErrorInfo interface.

    [RangeValidator(2, RangeBoundaryType.Inclusive,
                    -1, RangeBoundaryType.Ignore)]
    public int Size
    {
    }

And, more importantly, a UI that surfaced errors to the user in the form of highlighted fields and tooltips.

validation_ui

For many people this is just fine but it does have the limitation that if you want to change the validation rules you need to recompile the module. EntLib supports configuration based validation and we can use it to remove this limitation. The validation requirements can be decoupled and stored in a configuration file.

Validation through Configuration

Creating a validator which is configuration based is actually very easy. Simply pass an IConfigurationSource into the presentation model constructor and use this in the call to CreateValidator.

public class ConfiguratorPresentationModel :
     IConfiguratorPresentationModel,
     INotifyPropertyChanged,
     IDataErrorInfo
{
    public DelegateCommand<object> SetupCommand { get; private set; }
    private ValidationResults _validationResults;
    private readonly Validator<ConfiguratorPresentationModel> _validator;

    public ConfiguratorPresentationModel(IUniverse universe,
        IAddInLocatorService locator,
        IEventAggregator eventAggregator,
        [Dependency(ConfiguratorModule.ModuleName)]
        IConfigurationSource configuration)
    {
        // ...
        _validator = ValidationFactory
            .CreateValidator<ConfiguratorPresentationModel>(configuration);
        UpdateValidationState();
    }

    public int Size
    {
        get { return _size; }
        set
        {
            _size = value;
            OnNotifyPropertyChanged(PropertyNames.Size);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnNotifyPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler p = PropertyChanged;
        if (p != null)
            p(this, new PropertyChangedEventArgs(propertyName));
        UpdateValidationState();
    }

    private void UpdateValidationState()
    {
        _validationResults = _validator.Validate(this);
        SetupCommand.RaiseCanExecuteChanged();
    }

    public string this[string columnName]
    {
        get
        {
            ValidationResults filteredResults = new ValidationResults();

            filteredResults.AddAllResults(from result in _validationResults
                 where result.Key == columnName select result);
            return !filteredResults.IsValid ?
                 filteredResults.First().Message : null;
        }
    }

    public string Error
    {
        get { return null; }
    }

For those of you who read part 1 you’ll see that the code hasn’t really changed that much. The key here is how the IConfigurationSource gets built up by the Unity dependency injection (DI)container. You’ll see the configuration parameter has a Dependency attribute on it. What’s that all about?

EntLib’s configuration is usually stored in a (single) application configuration file. In this case I’d like to do something different. Because my application is made up from modules that may be developed separately and dynamically loaded at runtime I’d like each module to have it’s own configuration file. For example MyModule.dll would load it’s validation configuration from MyModule.dll.config.

To do this each module registers a named instance of an IConfigurationSource with the DI container. This instance loads a configuration file corresponding to the module name. Each presentation model is configured to take a named IConfigurationSource dependency – set using a Dependency attribute. When the Unity DI container creates the presentation model it injects the appropriate configuration source according to the ConfiguratorModule.ModuleName.

Here’s the code that registers the type with the container.

public class ConfiguratorModule : ModuleBase
{
    public override void Initialize()
    {
        // ...
        RegisterViewsAndServices();
    }

    private void RegisterViewsAndServices()
    {
        // ...
        Container.RegisterInstance<IConfigurationSource>(
            ConfiguratorModule.ModuleName,
            GetConfigurationSource());
    }

    private IConfigurationSource GetConfigurationSource()
    {
        if (File.Exists(ModuleConfigurationPath(this)))
            return new FileConfigurationSource(ModuleConfigurationPath(this));         return ConfigurationSourceFactory.Create();     }
}


The configuration source created in GetConfigurationSource. Note the fallback code that will return a configuration source corresponding to the application configuration. The configuration path is based on the assembly name but you can change this accordingly.

public class ModuleBase
{      protected static string ModuleConfigurationPath(IModule module)    {         return module.GetType().Assembly.GetName().Name + ".dll.config";     }
}


Now all that’s needed to to provide a configuration file to accompany the module assembly and make sure that it gets deployed alongside the assembly into the application’s working directory. This configuration implements the same same rule we previously implemented using attributes.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="validation"
             type="...Configuration.ValidationSettings, ..."/>
      </configSections>
      <validation>
        <type name="...ConfiguratorPresentationModel"
             defaultRuleset="default">
          <ruleset name="default">
            <properties>
              <property name="Size">
                <validator name="SizeRange"
                    type="...Validators.RangeValidator, ..."
                    lowerBound="2"
                    lowerBoundType="Inclusive"
                    upperBound="0"
                    upperBoundType="Ignore"/>
              </property>
            </properties>
          </ruleset>
        </type>
      </validation>
    </configuration>

Note: I’ve shortened a lot of the type names in the configuration above to make it more readable (so copy/paste will give you errors).

Hopefully this all helps you get configuration based validation for your presentation models up and running. I’ve really only covered the basics but it does show you how to create generic modules and apply (business rule based) validation through configuration.

If you’re thinking about WPF validation in general then something else you might want to check out is the Standalone Validation Block on EntLib Contrib project.

  1. 4 Responses to “Using the EntLib Validation Block with WPF Part 2: Configuration Based Validation”

  2. Hmmm, validation rules in xml files. Be sure to read this post (http://jeffreypalermo.com/blog/hardcoding-considered-harmful-or-is-it/) first and think about if you really need this.

    By Karsten on Feb 2, 2009

  3. Karsten,

    Some good points in that blog post. You’ll note that this is part 2 of 2. Part one dealt with attribute based validation which is hard coded. I think there’s a place for both. Hard coding means that your system is configured at design time and is therefore more testable (at design time). Configuration gives you more flexibility at run time, if you need it. In some cases you will in others you will not. I don’t think there’s a hard and fast rule here.

    Cheers,

    Ade

    By Ade Miller on Feb 2, 2009

  1. 2 Trackback(s)

  2. Feb 2, 2009: DotNetShoutout
  3. Mar 5, 2009: Using Prism With EntLib and Unity | #2782 - Agile software development, patterns and practices for building Microsoft .NET applications.

Sorry, comments for this entry are closed at this time.