Reputation: 2662
I'm already using the latest version of Fluent Validation which is 5.4.0.0; What I need to achieve is:
Think on a Vehicle VIN, which depends upon Make Id, Model Id and Year of manufacture. Does in Fluent I have the ability to execute a Rule based on more rules when these rules are successfully executed?
I must validate a vehicle vin if all three rules (Make, Model and Year) passes.
I've also read the whole documentation and couldn't find any reference to implement it.
class VehicleValidator : AbstractValidator<Vehicle>
{
public VehicleValidator()
{
RuleFor(v => v.MakeId)
.NotEmpty()
.WithMessage("Make Id is required")
RuleFor(v => v.ModelId)
.NotEmpty()
.WithMessage("Model Id is required")
RuleFor(v => v.Year)
.NotEqual(default(ushort))
.WithMessage("Year is required")
RuleFor(v => v)
.MustBeAValidVehicle()
.WithName("Vehicle")
.Unless(v => v.IdMarca == null || v.IdModelo == null && v.Ano == default(short));
RuleFor(v => v.VehicleVin)
.NotEmpty()
.WithMessage("Vehicle Vin is required")
//Only run this validation if the validation of MakeId, ModelId, Year and MustBeAValidVechile don't fail
RuleFor(v => v.VehicleVin)
.MustBeAValidVehicleVin()
.Unless(v => v.Chassi == null || v.IdMarca == null || v.IdModelo == null || v.Ano == default(ushort));
}
}
Upvotes: 0
Views: 3227
Reputation: 2662
I found out a workaround for my problem. Simply created a class which holds a boolean property to determine whether is true or false.
Please refer to my post on FLuent Validation discussion board ValidationResultHolder - Rule Dependency
Upvotes: 1
Reputation: 3324
You can combine the list of VIN validation rules using CascadeMode.StopOnFirstFailure
to ensure that you only see the first failure. This will prevent any additional processing.
public class VinValidator : AbstractValidator<Vin>
{
public VinValidator()
{
RuleFor(x => x.Vin)
.Cascade(CascadeMode.StopOnFirstFailure)
.Must(HaveValidMake).WithMessage("Invalid Make")
.Must(HaveValidModel).WithMessage("Invalid Model")
.Must(HaveValidYear).WithMessage("Invalid Year")
.Must(BeValidVin).WithMessage("Invalid VIN");
}
private bool HaveValidMake(string vin)
{
return vin.Contains("make");
}
private bool HaveValidModel(string vin)
{
return vin.Contains("model");
}
private bool HaveValidYear(string vin)
{
return vin.Contains("year");
}
private bool BeValidVin(string vin)
{
return vin.Length == 17;
}
}
The following examples demonstrate the behavior:
"invalid" => "Invalid Make"
"xxxmakexxx" => "Invalid Model"
"xxxmakemodelxxx" => "Invalid Year"
"xxxmakemodelyearxxx" => "Invalid VIN"
"xxxmakemodelyear" => valid
Update to reflect example code:
If there are no other validations being preformed in the AbstractValidator
, then you can set the class level property CascadeMode
to StopOnFirstFailure
to ensure that the validation stops on the first failure (see here). You can then keep the validation rules separate as you have in your example code.
Note: When testing, I noticed that the StopOnFirstFailure was not being respected for non-Must rules. You may need to convert your rules to Must
s as I have above.
Upvotes: 2