FluentValidation rule for multiple properties

C#Fluentvalidation

C# 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);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSofia KhwajaView Question on Stackoverflow
Solution 1 - C#bpruitt-goddardView Answer on Stackoverflow
Solution 2 - C#Matías RomeroView Answer on Stackoverflow
Solution 3 - C#Alan HintonView Answer on Stackoverflow
Solution 4 - C#Francesco D.M.View Answer on Stackoverflow