I found a way to break the circular reference:
- move the
SetValidator
calls out of the constructors, into anInitialize()
method. - reuse the validator instances across
SetValidator()
calls. For example, store validator instances as static fields in a static class.
Alright, I'm not that good with words, so here's the code:
The class that hods the validator instances and calls Initialize()
on them:
public static class Validators
{
public static Class1Validator Class1Validator = new Class1Validator();
public static Class2Validator Class2Validator = new Class2Validator();
public static Class3Validator Class3Validator = new Class3Validator();
static Validators()
{
Class1Validator.Initialize();
Class2Validator.Initialize();
Class3Validator.Initialize();
}
}
The validators now become:
public class Class1Validator : AbstractValidator<Class1>
{
public Class1Validator()
{
RuleFor(x => x.id).NotEqual(0).WithMessage("id is required.");
}
public void Initialize()
{
RuleFor(x => x.Class2).SetValidator(Validators.Class2Validator);
}
}
public class Class2Validator : AbstractValidator<Class2>
{
public Class2Validator()
{
RuleFor(x => x.id).NotEqual(0).WithMessage("id is required.");
}
public void Initialize()
{
RuleFor(x => x.Class3).SetValidator(Validators.Class3Validator);
}
}
public class Class3Validator : AbstractValidator<Class3>
{
public Class3Validator()
{
RuleFor(x => x.id).NotEqual(0).WithMessage("id is required.");
}
public void Initialize()
{
RuleFor(x => x.Class1).SetValidator(Validators.Class1Validator);
}
}
I have tested this and it seems to work OK. For example:
var validator = Validators.Class1Validator;
var c1 = new Class1
{
id = 1,
Class2 = new Class2
{
id = 2,
Class3 = new Class3
{
id = 3,
Class1 = new Class1
{
id = 0,
Class2 = null
}
}
}
};
var result = validator.Validate(c1);
result
is false here, because the inner Class1
instance has an Id
of 0
.
I was worried about the thread safety of this approach, but apparently it's OK, as stated here:
The validators are designed to only be instantiated once - they are thread safe.