I was looking for a validation rules implementation methodology on my business entities without breaking the SRP. I was comfortable using the validation application block and had some techniques to integrate with my WPF applications. Recently I stopped on the codeplex site on a project that uses fluent interfaces and lambda expressions for building validation rules on business objects. Jeremy Skinner has done a wonderful job by creating the Fluent Validation library. Using the library is easy and uses very little code for writing the validation rules.
I have created a sample application to demonstrate the usage of Fluent Validation.
public class Customer : BaseEntity<Guid>
{
public string Name { get; set; }
public string Email { get; set; }
public IList<Order> Orders { get; set; }
public Address Address { get; set; }
}
public class Order : BaseEntity<Guid>
{
public string Name { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
public int Quantity { get; set; }
}
The next step is to create your Validator classes for the entities.
public class OrderValidator : AbstractValidator<Order>
{
public OrderValidator()
{
RuleFor<string>(x => x.Name).NotNull().NotEmpty().WithMessage("Name cannot be null or empty").WithPropertyName("Name");
RuleFor<string>(x => x.Name).Length(1, 25)
.WithMessage("Name should be in the range 0 - 25").WithPropertyName("Name");
RuleFor<Guid>(x => x.Id).NotEqual(Guid.Empty).WithMessage("Id cannot be empty").WithPropertyName("Id");
RuleFor<DateTime>(x => x.Date).LessThan(DateTime.Today)
.WithMessage("Order date cannot be greater than today's date").WithPropertyName("Date");
RuleFor<DateTime>(x => x.Date).Must((x, y) => x.Date > new DateTime(1900, 01, 01))
.WithMessage("Order date should be greater than Jan, 01 1900").WithPropertyName("Date");
}
}
public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor<string>(x => x.Name).NotEmpty().NotNull()
.WithMessage("Name cannot be empty or null").WithPropertyName("Name");
RuleFor<string>(x => x.Name).Length(1, 25)
.WithMessage("Name should be of length 1 - 25").WithPropertyName("Name");
RuleFor<IList<Order>>(x => x.Orders).SetValidator(new OrderValidator());
RuleFor<Address>(x => x.Address).SetValidator(new AddressValidator());
RuleFor(x => x.Email).EmailAddress().WithMessage("Not a valid email").WithPropertyName("Email");
RuleFor(x => x.Address).NotNull().WithMessage("Customer cannot be created without address").WithPropertyName("Address");
}
}
Once the validators are defined you can validate the code like
[TestMethod]
public void Customer_with_invalid_email_id_is_created()
{
___Customer.Email = "invalid email";
var __Results = ___CustomerValidator.Validate(___Customer);
Assert.IsTrue(__Results.Errors.Count > 0);
Assert.IsFalse(__Results.IsValid);
Assert.IsTrue(__Results.Errors.Any(x => x.PropertyName == "Email"));
}
[TestMethod]
public void Customer_with_invalid_order_name_is_created()
{
___Customer.Orders[0].Name = "Invalid order name with more than 25 characters";
var __Results = ___CustomerValidator.Validate(___Customer);
Assert.IsTrue(__Results.Errors.Count > 0);
Assert.IsFalse(__Results.IsValid);
Assert.IsTrue(__Results.Errors.Any(x => x.PropertyName == "Orders[0].Name"));
}
I’m using DI for getting the reference to my validator classes.
Container = new UnityContainer()
.RegisterType<IValidator<Customer>, CustomerValidator>()
.RegisterType<IValidator<Address>, AddressValidator>()
.RegisterType<IValidator<Order>, OrderValidator>();
The validator instance is retrieved in the code from the container as
___CustomerValidator = Container.Resolve<IValidator<Customer>>();