验证抛出“无法将类型'BaseModel'的对象转换为类型'DerivedModel'”。

时间:2019-03-28 01:30:18

标签: c# fluentvalidation

当我尝试编写验证器时,我发现FluentValidation(v8.2.0)有一个奇怪的问题:

  

System.InvalidCastException     HResult = 0x80004002     Message =无法将类型为“ BaseModel”的对象转换为类型为“ DerivedModel”的对象。     来源= FluentValidation     堆栈跟踪:      在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:line 115中      在C:\ Users \ john \ Documents \ Visual Studio 2017 \ Projects \ TestApp \ TestApp \ Program.cs:line 76中的TestApp.Program.d__4.MoveNext()      在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()      在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)      在TestApp.Program。(字符串[]参数)

我的模型和验证者:

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 个答案:

答案 0 :(得分:1)

我实际上在我的Web应用程序中偶尔看到此问题-99%的时间它可以正常工作,但有时我会遇到此问题。我reached out向作者或FluentValidation的杰里米·斯金纳(Jeremy Skinner)解释了发生了什么事:

  

规则本质上与定义它们的验证器相关。不能将它们从一个验证器复制到另一个验证器。它们本质上与定义它们的验证器以及定义它们的类型相关。

     

每个条件块都有一个与之关联的唯一ID(允许条件条件的结果被缓存,因此它仅执行一个,而不针对其中的每个规则执行)。当您将规则从一个验证器复制到另一个验证器时,条件也会同时出现。

简而言之:您不能在验证者之间共享单个规则对象。

令人讨厌的代码段是DerivedModelValidator的以下代码段:

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

Jeremy针对此问题提供了两种不同的解决方案:

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。使用DerivedModelValidatorBaseModelValidator组成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);
    }
}