Reputation: 620
I'm trying to create Validation which is able to have two groups and block second validation if first fail (it contains many rules).
For now I did create a private 'BasicValidation' class inside and in 'main validator' do sth like this:
RuleFor(m => m).SetValidator(new BasicValidation()).DependentRules(() => {
//Complex validation
RuleFor(m => m.IdOfSthInDb)
.MustAsync(ItemMustExists)
.WithMessage("Item does not exist.");
});
It does the trick but I would like to avoid creating that 'BasicValidation' for each model.
Upvotes: 7
Views: 3304
Reputation: 6782
In my previous answer I misunderstood the question. The main goal is to avoid code duplication in different validators. After some investigation I found solution that matches your requirements. Suppose you have models:
public abstract class BaseModel
{
public string BaseProperty1 { get; set; }
public string BaseProperty2 { get; set; }
}
public class ChildModel : BaseModel
{
public int IdOfSthInDb { get; set; }
}
You have to create validator for base model (it would be used further):
class InternalBaseModelValidator : AbstractValidator<BaseModel>
{
public InternalBaseModelValidator()
{
RuleFor(x => x.BaseProperty1).NotEmpty().WithMessage("Property 1 is empty");
RuleFor(x => x.BaseProperty2).NotEmpty().WithMessage("Property 2 is empty");
}
}
Then you can use new feature of FluentValidation, called PreValidate:
public class BaseModelValidator<T>: AbstractValidator<T> where T : BaseModel
{
// necessary for reusing base rules
private readonly InternalBaseModelValidator preValidator;
protected BaseModelValidator()
{
preValidator = new InternalBaseModelValidator();
}
protected override bool PreValidate(ValidationContext<T> context, ValidationResult result)
{
var preValidationResult = preValidator.Validate(context.InstanceToValidate);
if (preValidationResult.IsValid)
{
return true;
}
foreach(var error in preValidationResult.Errors)
{
result.Errors.Add(new ValidationFailure(error.PropertyName, error.ErrorMessage, error.AttemptedValue));
}
return false;
}
}
After creating validator for all base models you can inherit from it for ChildModel validation:
public class ChildModelValidator : BaseModelValidator<ChildModel>
{
public ChildModelValidator()
: base()
{
RuleFor(x => x.IdOfSthInDb)
.MustAsync(ItemMustExists)
.WithMessage("Item does not exist.");
}
private Task<bool> ItemMustExists(int arg1, CancellationToken arg2)
{
return Task.FromResult(false); // some logic here
}
}
That's it!
Upvotes: 3
Reputation: 6782
I think hext code would solve your problem:
var basicValidator = new BasicValidation();
RuleFor(m => m).SetValidator(basicValidator));
When(m => basicValidator.Validate(m).IsValid, () =>
{
RuleFor(m => m.IdOfSthInDb)
.MustAsync(ItemMustExists)
.WithMessage("Item does not exist.");
});
Upvotes: 3