FluentValidation用于派生类型的SetCollectionValidator

时间:2014-07-25 04:36:54

标签: c# .net validation extension-methods fluentvalidation

如何在派生类型的集合项上设置验证器?

class BaseClass
{

}

class DerivedClass : BaseClass
{

}

class SomeClass
{
    public IEnumerable<BaseClass> BaseClasses { get; set; }
}

class DerivedClassValidator : AbstractValidator<DerivedClass>
{

}

class SomeClassValidator : AbstractValidator<SomeClass>
{
    public SomeClassValidator()
    {
        RuleFor(x => x.BaseClasses).????.SetCollectionValidator(new DerivedClassValidator);
    }
}

只是想知道......

有没有办法将它投射到像

这样的特定类型
RuleFor(x => x.SomeCollection).CastTo(typeof(SomeDerivedType)).SetCollectionValidator(new SomeDerivedValidator());

4 个答案:

答案 0 :(得分:4)

您可以使用条件包装规则来验证集合,其中包含不同派生类型的对象。

假设您有下一个类层次结构:

public class BaseClass
{
    public string Name { get; set; }
}

public class DerivedClassOne : BaseClass
{
    public int Count { get; set; }
}

public class DerivedClassTwo : BaseClass
{
    public double Price { get; set; }
}

包含BaseClass个对象集合的容器类:

public class ContainerClass
{
    public List<BaseClass> Collection { get; set; } 
}

主要思想是创建一个验证器类,负责所有类层次结构验证:

public class CommonBaseClassValidator : AbstractValidator<BaseClass>
{
    public CommonBaseClassValidator()
    {
        //common rule for all BaseClass types
        RuleFor(x => x.Name)
            .NotEmpty();

        // special rules for base type
        When(model => model.GetType() == typeof (BaseClass), () =>
        {
            RuleFor(x => x.Name)
                .Length(0, 10);
            // add rules here
        });

        //special rules for derived types
        When(model => model.GetType() == typeof(DerivedClassOne), () =>
        {
            RuleFor(model => ((DerivedClassOne) model).Count)
                .ExclusiveBetween(1, 9);
            // add rules here
        });

        When(model => model.GetType() == typeof(DerivedClassTwo), () =>
        {
            RuleFor(model => ((DerivedClassTwo) model).Price)
                .GreaterThan(1000);
            // add rules here
        });
    }
}

将此类注册为集合项验证器:

public class ContainerValidator : AbstractValidator<ContainerClass>
{
    public ContainerValidator()
    {
        RuleFor(model => model.Collection)
            .SetCollectionValidator(new CommonBaseClassValidator());
    }
}

答案 1 :(得分:3)

我创建了这个,以便更简单地处理这个场景:

public class DerivedValidatorBase<TBase> : AbstractValidator<TBase>
{
    public void MapDerivedValidator<TType, TValidatorType>()
        where TValidatorType : IEnumerable<IValidationRule>, IValidator<TType>, new()
        where TType: TBase
    {
        When(t => t.GetType() == typeof(TType), () => AddDerivedRules<TValidatorType>());
    }

    public void MapDerivedValidator<TType, TValidatorType>(TValidatorType validator)
        where TValidatorType : IEnumerable<IValidationRule>, IValidator<TType>
        where TType: TBase
    {
        When(t => t.GetType() == typeof(TType), () => AddDerivedRules<TValidatorType>(validator));
    }

    private void AddDerivedRules<T>(T validator)
        where T : IEnumerable<IValidationRule>
    {
        foreach (var rule in validator)
        {
            this.AddRule(rule);
        }
    }

    private void AddDerivedRules<T>()
        where T : IEnumerable<IValidationRule>, new()
    {
        IEnumerable<IValidationRule> validator = new T();
        foreach (var rule in validator)
        {
            this.AddRule(rule);
        }
    }
}

然后我只创建一个基本验证器类:

public class CommonBaseClassValidator : DerivedValidatorBase<BaseClass>
{
    public CommonBaseClassValidator()
    {
        MapDerivedValidator<DerivedClass, DerivedClassValidator>();
    }
}

或使用依赖注入时:

public class CommonBaseClassValidator : DerivedValidatorBase<BaseClass>
{
    public CommonBaseClassValidator(DerivedClassValidator validator)
    {
        MapDerivedValidator<DerivedClass, DerivedClassValidator>(validator);
    }
}

使用中:

RuleFor(v => v.BaseClasses).SetCollectionValidator(new CommonBaseClassValidator());

这样我就可以为派生类重用现有的验证器,我可能会在其他地方使用它们,并毫不费力地映射它们。

答案 2 :(得分:1)

6年后:请查看this link
按照说明进行操作,并在底部显示:

此方法也适用于集合,集合中的每个元素可能是不同的子类。

答案 3 :(得分:0)

我想添加一些帮助他人的东西。我受到Evgeny Levin解决方案的启发。 我更倾向于分隔每个验证器,而不是When中的规则列表。

public class CommonBaseClassValidator : AbstractValidator<BaseClass>
{
    public CommonBaseClassValidator()
    {
        //All rules for shared properties
        RuleFor(x => x.Name)
            .NotEmpty();

        RuleFor(x => x.Name)
                .Length(0, 10);

        //special rules for derived types
        When(model => model.GetType() == typeof(DerivedClassOne), 
            () => RuleFor(entity => entity as DerivedClassOne)
                    .SetValidator(new DerivedClassOneValidator()));

        When(model => model.GetType() == typeof(DerivedClassTwo), 
            () => RuleFor(entity => entity as DerivedClassTwo)
                .SetValidator(new DerivedClassTwoValidator()));
    }
}

希望有帮助。