我会给你一个选择,这样你就可以在这里实现你想要的,但是我不得不说我没有彻底考虑过任何副作用,所以请记住这一点。
无论属性值如何,您的验证器都将始终被设置,这就是为什么您在调用 ShouldHaveChildValidator 方法时不必传递任何对象的实例的原因。他们被执行与否的事实是另一回事,正如您所知,这将取决于您的规则集。
所以我克隆了 fluent validation git repo 并检查了代码如何检查子验证器的存在。
本次通话:
_validator.ShouldHaveChildValidator(i=>i.Property, typeof(FluentPropertyValidator));
这是做什么的:
- 它为您传递的属性表达式获取匹配的验证器
在方法调用中:
i => i.Property
- 它过滤匹配的验证器以仅获取
IChildValidatorAdaptor 类型的验证器。
- 如果选定的验证器都不能从您传递给方法调用的类型中分配,则会引发错误:
FluentPropertyValidator
似乎代码缺少验证器被另一个验证器包装的情况。 DelegatingValidator 类就是这种情况,顺便说一下,它是您的子验证器使用的类型。因此,一种可能的解决方案是同时考虑这些验证器类型。
我创建了一种扩展方法,您可以使用与原始方法相同的模式。由于我在命名事物时缺乏创造力(命名很难),我命名为ShouldHaveChildValidatorCustom。此方法与代码中的方法相同,它还调用了我刚刚从 FluentValidation 的源复制过来的几个其他方法,因此我可以添加小的修改。
这是完整的扩展类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using FluentValidation.Internal;
using FluentValidation.TestHelper;
using FluentValidation.Validators;
namespace YourTestExtensionsNamespace
{
public static class CustomValidationExtensions
{
public static void ShouldHaveChildValidatorCustom<T, TProperty>(this IValidator<T> validator, Expression<Func<T, TProperty>> expression, Type childValidatorType)
{
var descriptor = validator.CreateDescriptor();
var expressionMemberName = expression.GetMember()?.Name;
if (expressionMemberName == null && !expression.IsParameterExpression())
throw new NotSupportedException("ShouldHaveChildValidator can only be used for simple property expressions. It cannot be used for model-level rules or rules that contain anything other than a property reference.");
var matchingValidators = expression.IsParameterExpression() ? GetModelLevelValidators(descriptor) : descriptor.GetValidatorsForMember(expressionMemberName).ToArray();
matchingValidators = matchingValidators.Concat(GetDependentRules(expressionMemberName, expression, descriptor)).ToArray();
var childValidatorTypes = matchingValidators
.OfType<IChildValidatorAdaptor>()
.Select(x => x.ValidatorType);
//get also the validator types for the child IDelegatingValidators
var delegatingValidatorTypes = matchingValidators
.OfType<IDelegatingValidator>()
.Where(x => x.InnerValidator is IChildValidatorAdaptor)
.Select(x => (IChildValidatorAdaptor)x.InnerValidator)
.Select(x => x.ValidatorType);
childValidatorTypes = childValidatorTypes.Concat(delegatingValidatorTypes);
var validatorTypes = childValidatorTypes as Type[] ?? childValidatorTypes.ToArray();
if (validatorTypes.All(x => !childValidatorType.GetTypeInfo().IsAssignableFrom(x.GetTypeInfo())))
{
var childValidatorNames = validatorTypes.Any() ? string.Join(", ", validatorTypes.Select(x => x.Name)) : "none";
throw new ValidationTestException(string.Format("Expected property '{0}' to have a child validator of type '{1}.'. Instead found '{2}'", expressionMemberName, childValidatorType.Name, childValidatorNames));
}
}
private static IPropertyValidator[] GetModelLevelValidators(IValidatorDescriptor descriptor)
{
var rules = descriptor.GetRulesForMember(null).OfType<PropertyRule>();
return rules.Where(x => x.Expression.IsParameterExpression()).SelectMany(x => x.Validators)
.ToArray();
}
private static IEnumerable<IPropertyValidator> GetDependentRules<T, TProperty>(string expressionMemberName, Expression<Func<T, TProperty>> expression, IValidatorDescriptor descriptor)
{
var member = expression.IsParameterExpression() ? null : expressionMemberName;
var rules = descriptor.GetRulesForMember(member).OfType<PropertyRule>().SelectMany(x => x.DependentRules)
.SelectMany(x => x.Validators);
return rules;
}
}
}
如果您将子验证器设置为您的类,则该测试应该通过,否则失败:
[Fact]
public void ChildValidatorsSet()
{
var _validator = new FluentRemortgageInstructionValidator();
_validator.ShouldHaveChildValidatorCustom(i => i.Property, typeof(FluentPropertyValidator));
_validator.ShouldHaveChildValidatorCustom(i => i.AdditionalInformation, typeof(FluentAdditionalInformationValidator));
}
希望这会有所帮助!