Валидация выдает «Невозможно привести объект типа« BaseModel »к типу« DerivedModel ».» - PullRequest
1 голос
/ 28 марта 2019

При попытке создания валидаторов я вижу странную проблему с FluentValidation (v8.2.0):

System.InvalidCastException HResult = 0x80004002 Сообщение = Невозможно привести объект типа «BaseModel» к типу «DerivedModel». Источник = FluentValidation Трассировки стека: at FluentValidation.Internal.ConditionBuilder 1.<>c__DisplayClass2_0.<When>g__Condition|0(ValidationContext context) in C:\Projects\FluentValidation\src\FluentValidation\Internal\ConditionBuilder.cs:line 62 at FluentValidation.Internal.PropertyRule.<Validate>d__67.MoveNext() in C:\Projects\FluentValidation\src\FluentValidation\Internal\PropertyRule.cs:line 270 at System.Linq.Enumerable.SelectManySingleSelectorIterator 2.MoveNext () в System.Linq.Enumerable.WhereEnumerableIterator 1.MoveNext() at FluentValidation.AbstractValidator 1.Validate (контекст ValidationContext`1) в C: \ Projects \ FluentValidation \ src \ FluentValidation \ AbstractValidator.cs: строка 115 в TestApp.Program.d__4.MoveNext () в C: \ Users \ john \ Documents \ Visual Studio 2017 \ Projects \ TestApp \ TestApp \ Program.cs: строка 76 в System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (Задача) в TestApp.Program. (String [] args)

Моя модель и валидаторы:

public class BaseModel
{
    public string Name { get; set; }
    public bool IsAlive { get; set; }
}

public class DerivedModel : BaseModel
{
    public int Age { get; set; }
}

public class BaseModelValidator : AbstractValidator<BaseModel>
{
    public BaseModelValidator()
    {
        RuleFor(o => o.Name).Length(1, 20);
    }
}

public class DerivedModelValidator : AbstractValidator<DerivedModel>
{
    public DerivedModelValidator(BaseModelValidator baseValidator)
    {
        foreach (var rule in baseValidator)
        {
            AddRule(rule);
        }

        RuleFor(o => o.Age).GreaterThanOrEqualTo(0);
    }
}

Код, который я использую:

var baseModelValidator = new BaseModelValidator();
var derivedModelValidator = new DerivedModelValidator(baseModelValidator);

var baseModel = new BaseModel
{
    IsAlive = true,
    Name = "test2"
};
Console.WriteLine(baseModelValidator.Validate(baseModel).IsValid);

Как видите, я использую BaseModelValidator для проверки BaseModel, и нигде нет этой ссылки DerivedModel.

Как ни странно, если я уберу строку var derivedModelValidator = new DerivedModelValidator(baseModelValidator);, она будет работать без проблем.

Что вызывает это исключение и как его устранить?

1 Ответ

1 голос
/ 28 марта 2019

Я действительно периодически видел эту проблему в своем веб-приложении - в 99% случаев она работала бы нормально, но время от времени я получал эту проблему.Я обратился к Джереми Скиннеру, автору или FluentValidation, и он объяснил, что происходит:

Правила неразрывно связаны с валидаторами, которые их определяют.Они не предназначены для копирования из одного валидатора в другой.Они по своей сути связаны с валидатором, который их определил, и типом, с которым они были определены.

Каждый блок условий имеет уникальный идентификатор, связанный с ним (что позволяет кэшировать результат условия, поэтому он выполняется только один, а не для каждого правила внутри него).Когда вы копируете правила из одного валидатора в другой, условие также переносится.

Короче говоря: вы не можете разделить один объект правила между валидаторами.

Обидчикфрагмент кода: этот блок из DerivedModelValidator:

foreach (var rule in baseValidator)
{
    AddRule(rule);
}

Джереми предложил два разных решения этой проблемы:

1.Выведите оба класса валидаторов из общего базового класса валидатора, который содержит общие правила:

public abstract class CommonModelValidator<T> : AbstractValidator<T> where T : BaseModel
{
    protected CommonModelValidator()
    {
        RuleFor(o => o.Name).Length(1, 20);
    }
}

public class BaseModelValidator : CommonModelValidator<BaseModel>
{
}

public class DerivedModelValidator : CommonModelValidator<DerivedModel>
{
    public DerivedModelValidator(BaseModelValidator baseValidator) 
        : base()
    {
        RuleFor(o => o.Age).GreaterThanOrEqualTo(0);
    }
}

2.Составьте DerivedModelValidator из BaseModelValidator, используя SetValidator:

public class BaseModelValidator : AbstractValidator<BaseModel>
{
    public BaseModelValidator()
    {
        RuleFor(o => o.Name).Length(1, 20);
    }
}

public class DerivedModelValidator : AbstractValidator<DerivedModel>
{
    public DerivedModelValidator(BaseModelValidator baseValidator)
    {
        RuleFor(o => o).SetValidator(baseValidator);
        RuleFor(o => o.Age).GreaterThanOrEqualTo(0);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...