如何使用 FluentValidation 验证特定规则集(通过 IncludeRuleSets)中的 属性(通过 IncludeProperties)?
How to validate a property (via IncludeProperties) within a specific ruleset (via IncludeRuleSets) with FluentValidation?
花了一天时间搜索答案。
我有以下验证器(请注意 AgeMin 有不同的规则 属性 取决于规则集。):
class WeirdValidator : AbstractValidator<TestClass>
{
public WeirdValidator()
{
public const string CustomRuleSet = nameof(CustomRuleSet);
RuleFor(instance => instance.AgeMin).NotEmpty().WithMessage("Empty error. {PropertyName}").ScalePrecision(2, 3).WithMessage("Scale error. {PropertyName}");
RuleFor(instance => instance.FirstInt).NotEmpty().WithMessage("Empty error. {PropertyName}");
RuleSet(CustomRuleSet, () => {
RuleFor(instance => instance.AgeMin).Empty().WithMessage("{PropertyName} must be empty.");
RuleFor(instance => instance.SecondInt).NotEmpty().WithMessage("{PropertyName} must not be empty.");
});
}
}
和以下测试class:
class TestClass
{
public decimal AgeMin { get; set; }
public int FirstInt { get; set; }
public int SecondInt { get; set; }
}
FluentValidation 允许我们组合验证器:
var result = validator.Validate(instanceToValidate, opt => {
opt.IncludeRuleSets(WeirdValidator.CustomRuleSet);
opt.IncludeProperties(nameof(instanceToValidate.AgeMin));});
但它并没有像我预期的那样工作。它不是规则的交集,而是后续操作:首先应用来自 CustomRuleSet 的所有规则,然后应用定义的 属性 的规则。我只需要验证仅选定规则集的选定 属性。
我相信有一个非常简单和优雅的解决方案,但它还没有照亮我的路。
问题:
如何验证特定规则集中的特定 属性?
经过几天的痛苦和流泪,我找到了一个看起来很糟糕的解决方案,但仍然有效。
var validator = new WeirdValidator();
var ruleDescriptor = validator.CreateDescriptor();
var rulesOfMember = ruleDescriptor.GetRulesForMember(nameof(instanceToValidate.AgeMin));
var exactRule = rulesOfMember.FirstOrDefault(c => c.RuleSets.Contains(WeirdValidator.CustomRuleSet));
var errorList = exactRule.Validate(
new ValidationContext<TestClass>(instanceToValidate, new PropertyChain(),
new RulesetValidatorSelector(WeirdValidator.CustomRuleSet)));
主要思想是找到精确 属性 的精确规则并只执行一个。原来 IValidationRule 有自己的 Validate/ValidateAsync 方法。
如果有人知道better/more优雅的解决方案,欢迎您。
您可以创建自定义 IValidatorSelector,并在创建验证时使用它 context.You 可以使用 FluentValidation.ValidatorOptions.Global.ValidatorSelectors
中提供的工厂来创建所需的选择器。
public class FluentValidatorCompositeValidatorSelector : IValidatorSelector
{
private IEnumerable<IValidatorSelector> _selectors;
public FluentValidatorCompositeValidatorSelector(IEnumerable<IValidatorSelector> selectors)
{
_selectors = selectors;
}
public bool CanExecute(IValidationRule rule, string propertyPath, IValidationContext context)
{
return _selectors.All(s => s.CanExecute(rule, propertyPath, context));
}
}
这将过滤所有不满足 ALL 选择器的规则。
我将整个东西添加到扩展方法中以简化其使用。希望将来会有更好的解决方案。这是扩展方法,如果有人出于某种原因需要它。
public static class ValidatorExtensions
{
public static async Task<ValidationResult> ValidateAsync<T>(
this IValidator<T> @this,
T instance,
IEnumerable<string> properties,
IEnumerable<string> ruleSets,
CancellationToken cancellationToken)
{
var memberNameValidator = ValidatorOptions
.Global
.ValidatorSelectors
.MemberNameValidatorSelectorFactory(properties);
var ruleSetValidator = ValidatorOptions
.Global
.ValidatorSelectors
.RulesetValidatorSelectorFactory(ruleSets);
var validationContext = new ValidationContext<T>(
instance,
new PropertyChain(),
new FluentValidatorCompositeValidatorSelector(new[] {
memberNameValidator,
ruleSetValidator
}));
var validationResult = await @this.ValidateAsync(validationContext, cancellationToken);
return validationResult;
}
}
花了一天时间搜索答案。
我有以下验证器(请注意 AgeMin 有不同的规则 属性 取决于规则集。):
class WeirdValidator : AbstractValidator<TestClass>
{
public WeirdValidator()
{
public const string CustomRuleSet = nameof(CustomRuleSet);
RuleFor(instance => instance.AgeMin).NotEmpty().WithMessage("Empty error. {PropertyName}").ScalePrecision(2, 3).WithMessage("Scale error. {PropertyName}");
RuleFor(instance => instance.FirstInt).NotEmpty().WithMessage("Empty error. {PropertyName}");
RuleSet(CustomRuleSet, () => {
RuleFor(instance => instance.AgeMin).Empty().WithMessage("{PropertyName} must be empty.");
RuleFor(instance => instance.SecondInt).NotEmpty().WithMessage("{PropertyName} must not be empty.");
});
}
}
和以下测试class:
class TestClass
{
public decimal AgeMin { get; set; }
public int FirstInt { get; set; }
public int SecondInt { get; set; }
}
FluentValidation 允许我们组合验证器:
var result = validator.Validate(instanceToValidate, opt => {
opt.IncludeRuleSets(WeirdValidator.CustomRuleSet);
opt.IncludeProperties(nameof(instanceToValidate.AgeMin));});
但它并没有像我预期的那样工作。它不是规则的交集,而是后续操作:首先应用来自 CustomRuleSet 的所有规则,然后应用定义的 属性 的规则。我只需要验证仅选定规则集的选定 属性。 我相信有一个非常简单和优雅的解决方案,但它还没有照亮我的路。
问题: 如何验证特定规则集中的特定 属性?
经过几天的痛苦和流泪,我找到了一个看起来很糟糕的解决方案,但仍然有效。
var validator = new WeirdValidator();
var ruleDescriptor = validator.CreateDescriptor();
var rulesOfMember = ruleDescriptor.GetRulesForMember(nameof(instanceToValidate.AgeMin));
var exactRule = rulesOfMember.FirstOrDefault(c => c.RuleSets.Contains(WeirdValidator.CustomRuleSet));
var errorList = exactRule.Validate(
new ValidationContext<TestClass>(instanceToValidate, new PropertyChain(),
new RulesetValidatorSelector(WeirdValidator.CustomRuleSet)));
主要思想是找到精确 属性 的精确规则并只执行一个。原来 IValidationRule 有自己的 Validate/ValidateAsync 方法。
如果有人知道better/more优雅的解决方案,欢迎您。
您可以创建自定义 IValidatorSelector,并在创建验证时使用它 context.You 可以使用 FluentValidation.ValidatorOptions.Global.ValidatorSelectors
中提供的工厂来创建所需的选择器。
public class FluentValidatorCompositeValidatorSelector : IValidatorSelector
{
private IEnumerable<IValidatorSelector> _selectors;
public FluentValidatorCompositeValidatorSelector(IEnumerable<IValidatorSelector> selectors)
{
_selectors = selectors;
}
public bool CanExecute(IValidationRule rule, string propertyPath, IValidationContext context)
{
return _selectors.All(s => s.CanExecute(rule, propertyPath, context));
}
}
这将过滤所有不满足 ALL 选择器的规则。
我将整个东西添加到扩展方法中以简化其使用。希望将来会有更好的解决方案。这是扩展方法,如果有人出于某种原因需要它。
public static class ValidatorExtensions
{
public static async Task<ValidationResult> ValidateAsync<T>(
this IValidator<T> @this,
T instance,
IEnumerable<string> properties,
IEnumerable<string> ruleSets,
CancellationToken cancellationToken)
{
var memberNameValidator = ValidatorOptions
.Global
.ValidatorSelectors
.MemberNameValidatorSelectorFactory(properties);
var ruleSetValidator = ValidatorOptions
.Global
.ValidatorSelectors
.RulesetValidatorSelectorFactory(ruleSets);
var validationContext = new ValidationContext<T>(
instance,
new PropertyChain(),
new FluentValidatorCompositeValidatorSelector(new[] {
memberNameValidator,
ruleSetValidator
}));
var validationResult = await @this.ValidateAsync(validationContext, cancellationToken);
return validationResult;
}
}