【问题标题】:How to validate a property (via IncludeProperties) within a specific ruleset (via IncludeRuleSets) with FluentValidation?如何使用 FluentValidation 在特定规则集中(通过 IncludeRuleSets)验证属性(通过 IncludeProperties)?
【发布时间】:2020-10-30 14:55:05
【问题描述】:

花了一天时间寻找答案。

我有以下验证器(请注意,AgeMin 属性有不同的规则取决于 RuleSet。):

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 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 中的所有规则,然后应用定义属性的规则。我需要验证仅选定规则集的选定属性。 我确信有一个非常简单和优雅的解决方案,但它还没有照亮我的道路。

问题: 如何验证特定规则集中的特定属性?

【问题讨论】:

  • 我不太熟悉这种用法,但我观察到相同的行为;如果您指定规则集和属性,它会执行规则集和验证器中指定属性的任何规则。我在 FV 问题列表中看不到任何关于它的内容,如果没有解决方案,值得将其作为一个问题提出。
  • 感谢您的回复!我似乎找到了答案并将发布答案。
  • 感谢您发布您的答案。我仍然会考虑将其作为一个问题提出,因为向我阅读 IncludeRuleSets 的用法表明它应该只执行指定规则集中的规则。如果您包含 default 或 IncludeRulesNotInRuleSet,我希望观察到的行为。

标签: c# fluentvalidation


【解决方案1】:

您可以创建自定义 IValidatorSelector,并在创建验证上下文时使用它。您可以使用 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 选择器的规则。

【讨论】:

    【解决方案2】:

    我将整个内容添加到扩展方法中以简化其使用。希望未来会有更好的解决方案。如果有人出于某种原因需要,这里是扩展方法。

    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;
        }
    }
    

    【讨论】:

    【解决方案3】:

    经过几天的痛苦和流泪,我找到了一个看起来很糟糕的解决方案,但仍然有效。

    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 方法。

    如果有人知道更好/更优雅的解决方案,欢迎您。

    【讨论】:

    • 我的 IValidationRule 没有验证方法,我使用的是 10.0.0 版。想知道您使用的是什么版本,或者您是否找到了更优化的代码?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多