FluentValidation rule for multiple properties
C#FluentvalidationC# Problem Overview
I have a FluentValidator that has multiple properties like zip and county etc. I want to create a rule that takes two properties just like a RuleFor construct
public class FooArgs
{
public string Zip { get; set; }
public System.Guid CountyId { get; set; }
}
public class FooValidator : AbstractValidator<FooArgs>
{
RuleFor(m => m.CountyId).Must(ValidZipCounty).WithMessage("wrong Zip County");
}
This works but I want to pass both Zip and county to the rue in order to validate. What is the best method to achieve this?
C# Solutions
Solution 1 - C#
There is a Must
overload that also provides you with the FooArgs
object documented here. It allows you to easily pass both arguments into your method like this:
RuleFor(m => m.CountyId).Must((fooArgs, countyId) =>
ValidZipCounty(fooArgs.Zip, countyId))
.WithMessage("wrong Zip County");
Solution 2 - C#
What about:
RuleFor(m => new {m.CountyId, m.Zip}).Must(x => ValidZipCounty(x.Zip, x.CountyId))
.WithMessage("Wrong Zip County");
Solution 3 - C#
Just came across this old question and I think I have a simpler answer. You can easily pass your whole object into your custom validation rule by simplifying the parameter to RuleFor
e.g.
RuleFor(m => m).Must(fooArgs =>
ValidZipCounty(fooArgs.Zip, fooArgs.countyId))
.WithMessage("wrong Zip County");
If the ValidZipCountry
method is local to your validator and you can change its signature to take a FooArgs
then the code simplifies down to
RuleFor(m => m).Must(ValidZipCounty).WithMessage("wrong Zip County");
The only downside is that the PropertyName
in the resultant validation error will be an empty string. This may cause a problem for you validation display code. However it is not really clear which property the error belongs too, ContryId
or Zip
, so this does make sense.
Solution 4 - C#
In my case I needed to mark a property as required (x.RequiredProperty
in the example below) if another property was not null (x.ParentProperty
in the example below). I ended up using the When
syntax:
RuleFor(x => x.RequiredProperty).NotEmpty().When(x => x.ParentProperty != null);
Or if you have more then one rule for a common when clause you can write it as follows:
When(x => x.ParentProperty != null, () =>
{
RuleFor(x => x.RequiredProperty).NotEmpty();
RuleFor(x => x.OtherRequiredProperty).NotEmpty();
});
The definition of the When
syntax is the following:
/// <summary>
/// Defines a condition that applies to several rules
/// </summary>
/// <param name="predicate">The condition that should apply to multiple rules</param>
/// <param name="action">Action that encapsulates the rules.</param>
/// <returns></returns>
public IConditionBuilder When (Func<T, bool> predicate, Action action);