Как повторно использовать данные в 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; });

Есть ли лучший способ отображения сообщений об ошибках, не все время писать эти лямбда-выражения для извлечения свойства? Например, сохранить модель где-то, а затем использовать ее позже.

просто и легко реализовать решения было бы неплохо!

2 5

2 ответа:

Для #1, боюсь, нет способа сделать это. Валидаторы должны быть без состояния, чтобы их можно было повторно использовать в потоках (на самом деле, настоятельно рекомендуется создавать экземпляры валидаторов в виде синглетов, поскольку их создание очень дорого. Интеграция MVC делает это по умолчанию). Не связывайтесь со статическими полями, так как вы столкнетесь с потоковыми проблемами.

(Edit: в этом конкретном простом случае вы можете просто объединить правила в один вызов Must, но в целом вы не можете разделите состояние между правилами)

Для #2 это зависит от используемого валидатора свойств. Большинство валидаторов свойств фактически позволяют использовать заполнитель {PropertyValue}, и значение будет вставлено автоматически. Однако в этом случае вы используете валидатор" Must " (PredicateValidator), который не поддерживает заполнители.

У меня есть список валидаторов, которые поддерживают пользовательские заполнители здесь: https://github.com/JeremySkinner/FluentValidation/wiki/c.-Built-In-Validators

Часть 1

Вы хотите уменьшить число вызовов базы данных с 2 до 1, поэтому вам нужно использовать поле для сохранения результата вызова базы данных, потому что правила валидатора код на самом деле работают в "runtime".

Класс валидатора:

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# в глубину":

Когда лямбда-выражение нуждается только в одном параметре, и это параметр может быть неявно типизирован, C# 3 позволяет опустить скобки, так что теперь у него есть это форма

GOTCHAS:

  1. Не передавайте явно this лямбда-выражениям. Насколько я знаю,это может вызвать проблемы с подготовкой. Нет никакой причины создавать экстра-закрытие.

  2. Я полагаю, что вы используете DataContext в некоторой форме внутри метода GetDataDataFromDB. Таким образом, вы должны контролировать время жизни вашего контекста, потому что объект validator создается как синглетный.