Pregunta

I have three entities as follows and they are linked with each other as follows:

class Class1 
{ 
   int id { get; set; }
   Class2 { get; set; } 
}

class Class2 
{
   int id { get; set; }
   Class3 { get; set; } 
}

class Class3 
{ 
   int id { get; set; }
   Class1 { get; set; } 
}

Now I created validators for each of these class and also have SetValidator for the entities linked to them:

//Class1 (and other classes) validator has following rules:
public class Class1Validator()
{
    public Class1Validator()
    {
        RuleFor(x => x.id).NotEqual(0).WithMessage("id is required.");
        RuleFor(x => x.Class2).SetValidator(new Class2Validator());
    }
}
public class Class2Validator()
{
    public Class2Validator()
    {
        RuleFor(x => x.id).NotEqual(0).WithMessage("id is required.");
        RuleFor(x => x.Class3).SetValidator(new Class3Validator());
    }
}
public class Class3Validator()
{
    public Class3Validator()
    {
        RuleFor(x => x.id).NotEqual(0).WithMessage("id is required.");
        RuleFor(x => x.Class1).SetValidator(new Class1Validator());
    }
}

//Now when following is called:
var validator = new Class1Validator();

When I check validate for Class1, it also contains validator for Class2 and then Class2 has for Class3, and finally at the end Class3 also has validator for Class1 which forms an infinite loop and throws the following error:

An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

and when viewing full details it shows:

Cannot evaluate expression because the current thread is in a stack overflow state.

Any solution for this or am I doing something wrong here?


Update: I also tried the below where I can restrict the validator if object is null, but this didnt work:

When(x => x.Class1 != null, () => RuleFor(x => x.Class1).SetValidator(new Class1Validator()));
¿Fue útil?

Solución

I found a way to break the circular reference:

  • move the SetValidator calls out of the constructors, into an Initialize() 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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top