流畅验证 - 将验证与特定流程最佳实践联系起来

时间:2012-06-12 09:45:11

标签: c# validation c#-4.0 moq fluentvalidation

使用http://fluentvalidation.codeplex.com/

将验证规则绑定到特定流程的最佳做法是什么?

目前我正在使用“规则集”功能将规则分组到不同的进程:

 public class ObjAValidation: AbstractValidator<A>
    {
        public ObjAValidation()
        {
            RuleSet("ProcessA", () =>
            {
                RuleFor(x => ...);
                RuleFor(x => ...);
            }); 
           RuleSet("ProcessB", () =>
            {
                RuleFor(x => ...);
                RuleFor(x => ...);
            }); 
        }
    }

然后使用以下方式验证:

var a = new A(){...};
IValidator<A> validator = new ObjAValidation();
var result = validator.Validate(a, ruleSet: "ProcessA");

这种方法有两个问题:

  1. 我不喜欢使用字符串作为进程名称。我想用一个 更强类型的方法。例如,能够使用标记 接口或属性。
  2. 在我的单元测试中,我无法设置IValidator的Validate方法 因为你不能在Moq中使用可选参数。
  3. 
    Mock<IValidator<A>> _mockValidator = new Mock<IValidator<A>>();
    _mockValidator.Setup(x => x.Validate(new A(), ruleSet: "ProcessA"));
    
    第二行生成运行时错误:表达式树可能不包含命名参数规范。如果要将没有命名参数的ruleSet参数传递给Validate方法,则必须提供“IValidatorSelector选择器” “对象。但是没有记录这个界面。

2 个答案:

答案 0 :(得分:0)

是什么阻止您创建一个帮助器类,您可以使用变量,数据结构,任何您喜欢的东西来阻止使用硬编码字符串参数?另外,不要忘记使用枚举的可能性。

是什么阻止您创建一个实现IValidator的类,您还可以在其中实现您需要的自定义功能?

答案 1 :(得分:0)

我遇到了同样的问题,并找出了你不能通过Moq等模拟这个方法的原因validator.Validate(a, ruleSet: "ProcessA");是因为它是一个扩展方法,Moq不能模拟静态方法。

有人在FluentValidation问题中提出了同样的问题:https://github.com/JeremySkinner/FluentValidation/issues/191

简单的解决方案是不使用扩展方法而是使用即时方法IValidator.validate(context)。您需要做的就是构建上下文。从这里查看源代码:https://github.com/JeremySkinner/FluentValidation/blob/master/src/FluentValidation/DefaultValidatorExtensions.cs#L819

if(ruleSet != null) {
                var ruleSetNames = ruleSet.Split(',', ';').Select(x => x.Trim());
                selector = ValidatorOptions.ValidatorSelectors.RulesetValidatorSelectorFactory(ruleSetNames.ToArray());
            } 

            var context = new ValidationContext<T>(instance, new PropertyChain(), selector);
            return validator.Validate(context);