Introduction
Validation is often the bane of a development project. It's critical to usability and data integrity (and often security, when ensuring data is not an attempt to attack the application), but it can be very time consuming to write the validation code and handle all of the permutations. Luckily Jeremy Skinner has created an excellent open source validation framework called Fluent Validation for .NET. Jeremy has created a lot of documentation, so this article will not cover the features and usage of Fluent Validation for .NET. One feature the documentation does not cover is how to pass the validation object via dependency injection when using Unity
, which is an Inversion of Control (IoC) container. This article addresses how to do this. For the attached example I've used Silverlight, but the same techniques can be used with WPF.
Background
Dependency injection is a useful pattern to acheive loose coupling and also simplify testing by allowing dependencies to be stubbed or mocked. By being able to pass a validation object into a business object or view model (in the case of MVVM), tests can be written for the business object or view model without requiring the business object or view model to be valid. The validation object can be stubbed to always pass or fail as required.
This article does not cover unit tests, mocking, and dependency injection. It is assumed that the reader is already conversant with these techniques.
Using the code
The first step is to create a Unity
validator factory from the ValidatorFactoryBase
provided by Fluent Validation for .NET. The important code is the override for CreateInstance
, which allows the factory to return the correct validation object from the Unity
container.
public class UnityValidatorFactory : ValidatorFactoryBase
{
private readonly IUnityContainer _container;
public UnityValidatorFactory(IUnityContainer container)
{
_container = container;
}
public override IValidator CreateInstance(Type validatorType)
{
return _container.Resolve(validatorType) as IValidator;
}
}
The next step is to register the factory with the Unity
container. In the attached example, this is done in the Bootstrapper.cs
class.
Container.RegisterType<IValidatorFactory, UnityValidatorFactory>(new ContainerControlledLifetimeManager());
Now the validation class can be created by inheriting from the AbstractValidator
base class. See Fluent Validation for .NET documentation for more information about how to build validation rules from this base class.
public class MainPageViewModelValidator : AbstractValidator<IMainPageViewModel>
{
public MainPageViewModelValidator()
{
RuleFor(x => x.FirstName)
.NotEmpty()
.WithMessage("First name cannot be blank.");
RuleFor(x => x.LastName)
.NotEmpty()
.WithMessage("Last name cannot be blank.");
RuleFor(x => x.Age)
.NotEmpty()
.WithMessage("Age is required.");
RuleFor(x => x.Age)
.Must(a => a.IsInt32())
.When(x => !x.Age.IsNullOrEmpty())
.WithMessage("Age must be an integer.");
}
}
The validation class must also be registered with Unity
. In the attached example, this is also registered in Bootstrapper.cs
.
The IValidator
generic interface is assigned the interface for the view model and the registered implementation is the view model validator.
Container.RegisterType<IValidator<IMainPageViewModel>, MainPageViewModelValidator>(new ContainerControlledLifetimeManager());
Now a ViewModel
can access the validation object by passing the validation factory in as a dependency and requesting the required validation object.
using FluentValidation;
private readonly IValidator<IMainPageViewModel> _validator;
public MainPageViewModel(IValidatorFactory validatorFactory)
{
_validator = validatorFactory.GetValidator<IMainPageViewModel>();
}
The validation object can then be used as required, either validating the whole view model, or just a property (which can be useful when implementing IDataErrorInfo
, as shown in the attached example). An example of validating a property is shown below. Note that a validation object can return more than one error, so the ValidationResult
contains an Errors
collection.
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName != value)
{
_firstName = value;
RaisePropertyChanged("FirstName");
}
ClearError("FirstName");
var validationResult = _validator.Validate(this, "FirstName");
if (!validationResult.IsValid)
{
validationResult.Errors.ToList().ForEach(x => SetError("FirstName", x.ErrorMessage));
}
}
}
History
- July 2011 - initial release.