I really like Jeremy Skinner’s FluentValidation library and have used it on a couple of projects. Up until now though, I’ve only be using it to validate single properties in isolation.
The other day a colleague and I were trying to create a rule that validated two properties by querying the database and checking that the combination didn’t already exist. But we couldn’t seem to work it out.
I cheekily pinged @JeremySkinner and he helpfully pointed out that there are several ways of validating multiple properties:
- if you want to do cross property comparison then RuleFor(x => x.Foo).GreaterThan(x => x.Bar) etc
- you can also use Must: RuleFor(x => x.Foo).Must((instance, foo) => …)
- usually those cover most scenarios. If not, then there’s also Custom, but that’s usually a last resort
The second option was what worked for us, and here is an example.
public class FooBarRequestValidator : AbstractValidator<FooBarRequest> { private readonly IRepository repository; public FooBarRequestValidator(IRepository repository) { this.repository = repository; RuleFor(x => x.Foo) .Must(NotAlreadyHaveABar) .WithMessage("You already have a bar for this foo."); } private bool NotAlreadyHaveABar(FooBarRequest instance, string foo) { return !repository.GetAll<FooBar>() .Any(x => x.Foo == foo && x.Bar == instance.Bar); } }
The trick is using the overload of Must()
that takes a Func<T, TProperty, bool>
predicate: that gives you access to the whole instance for your validating pleasure. Here, in NotAlreadyHaveABar()
I can access Bar
even though the rule is for the Foo
property.
This works with FluentValidation v1.1 up to v2.0 Beta 1.