如何在FluentValidation中重用数据

时间:2016-05-27 13:27:36

标签: c# fluentvalidation

例如,我有两个验证规则的验证器:

// Rule 1
RuleFor(o => o.Email).Must((email) => this.GetDataDataFromDB(email) != 0)
    .WithMessage("User with provided Email was not found in database!");

// Rule 2
RuleFor(o => o.Email).Must((email) => this.GetDataDataFromDB(email) >= 1)
    .WithMessage("There are multiple users with provided Email in database!");

正如您所看到的,使用相同的方法对数据库进行了两次调用。如何调用一次并将数据重用于其他规则?
显示错误消息时的另一个问题:

RuleFor(o => o.Email).Must((email) => this.GetDataDataFromDB(email) >= 1)
    .WithMessage("There are multiple users with following Email '{0}' in database!",
    (model, email) => { return email; });

有没有更好的方法来显示错误消息,而不是一直写这些lambda表达式来检索属性?就像在某处保存模型一样,然后再使用它。

简单易行的解决方案很不错!

2 个答案:

答案 0 :(得分:4)

对于#1,恐怕没有办法做到这一点。验证器被设计为无状态,因此它们可以跨线程重用(事实上,强烈建议您将验证器实例创建为单例,因为实例化它们非常昂贵。默认情况下MVC集成执行此操作)。不要乱用静态字段,因为你会遇到线程问题。

(编辑:在这个特殊的简单情况下,您可以将规则合并为一次调用Must,但一般情况下您不能在规则之间共享状态)

对于#2,这取决于您正在使用的属性验证器。大多数属性验证器实际上允许您使用{PropertyValue}占位符,并且将自动插入该值。但是,在这种情况下,您使用的是“Must”验证器(PredicateValidator),它不支持占位符。

我在此处列出了哪些验证程序支持自定义占位符:https://github.com/JeremySkinner/FluentValidation/wiki/c.-Built-In-Validators

答案 1 :(得分:1)

第1部分

您希望将数据库调用从2减少到1,因此您需要使用字段来保存数据库调用结果,因为验证器规则代码实际上可以在"运行时&#34中工作; 即可。

验证员类:

public class MyValidator : Validator<UserAccount>
{
    private int? _countOfExistingMails;
    private string _currentEmail;
    private object locker = new object();

    public MyValidator()
    {
        CallEmailValidations();
        // other rules...
    }
}

这是邮件验证调用的单独方法。至于Must将表达式作为参数,您可以使用它的参数传递方法名称:

public void CallEmailValidations()
{
    RuleFor(o => o.Email).Must(x => EmailValidation(x, 0))
        .WithMessage("User with provided Email was not found in database!");

    RuleFor(o => o.Email).Must(x => EmailValidation(x, 1))
        .WithMessage("There are multiple users with provided Email in database!");
}

验证方法的身体本身:

public bool EmailValidation(string email, int requiredCount)
{
    var isValid = false;

    lock(locker)
    {
        if (email != _currentEmail || _currentEmail == null)
        {
            _currentEmail = email;
            _countOfExistingMails = (int)GetDataDataFromDB(email);
        }

        if (requiredCount == 0)
        {
            isValid = _countOfExistingMails != 0; // Rule 1
        }
        else if (requiredCount == 1)
        {
            isValid = _countOfExistingMails <= 1; // Rule 2
        }
    }
    // Rule N...

    return isValid;
}

更新的: 此代码有效,但更好的方法是在数据访问层方法中实现缓存。

第2部分

这是重写的规则:

RuleFor(o => o.Email).Must((email) => GetDataDataFromDB(email) >= 1)
    .WithMessage("There are multiple users with following Email '{0}' in database!", m => m.Email)

来自"C# in depth"

  

当lambda表达式只需要一个参数时,那就是   参数可以隐式输入,C#3允许你省略   括号,所以它现在有这种形式

<强>陷阱:

  1. 不要将this显式传递给lambda表达式。据我所知,它可能会导致性能问题。没有理由创建额外的闭包。

  2. 我想您在DataContext方法中以某种形式使用GetDataDataFromDB。所以你必须控制你的上下文的生命周期,因为验证器对象被实例化为单一的。