Проверка уровня обслуживания

Я пытаюсь реализовать стратегию проверки в своем приложении. У меня есть уровень MVC, уровень сервиса, репозиторий и доменные POCOs. Теперь на уровне MVC я использую аннотации данных на своих моделях просмотра для проверки ввода пользователя, что позволяет мне дать пользователю обратную связь. В контроллере я вызываю ModelState.IsValid для проверки ввода перед использованием automapper для настройки объекта домена.

Здесь, где моя беда. Я передаю свой объект домена в Службу, которая должна проверять его против моих бизнес-правил, но как я могу пройти проверки ошибок обратно на контроллер? Примеры, которые я нашел, выполняют одно из следующих действий:

  • Выбросьте исключение на уровне сервиса и поймайте в Contoller. Но это кажется неправильным, конечно исключения для исключительных случаев, и мы должны вернуть что-то значимое.
  • Используйте ModelStateWrapper и вставьте ModelStateDictionary в Сервис. Но этот метод в конечном счете разворачивается в круговую зависимость (контроллер зависит от сервиса, сервиса зависит от контроллера), и это, кажется, плохой запах кода.
  • Добавить метод проверки в POCO. Проблема заключается в том, что бизнес-правило может полагаться на другие объекты POCO, поэтому это должно быть сделано в Службе, которая имеет доступ к необходимым таблицам и объектам.

Есть ли более простой метод, который мне не хватает? Я видел много вопросов относительно этого, но не было конкретного решения, кроме упомянутых выше. Я думаю, что любой метод в службе, который выполняет проверку, может просто передать некоторый объект key/value, который я могу использовать в контроллере, но я не уверен, что эта стратегия может быть проблематичной позже.

Ответы

Ответ 1

Я думаю, что довольно элегантный способ состоял бы в том, чтобы на вашем сервисе был установлен метод проверки достоверности, который возвращает словарь ошибок модели из бизнес-логики. Таким образом, в приложение не входит инъекция ModelState - служба просто выполняет проверку и возвращает любые ошибки. Затем контроллер должен слить эти ошибки ModelState обратно в него ViewData.

Таким образом, метод проверки службы может выглядеть так:

public IDictionary<string, string> ValidatePerson(Person person)
{
    Dictionary<string, string> errors = new Dictionary<string, string>();

    // Do some validation, e.g. check if the person already exists etc etc

    // Add model erros e.g.:
    errors.Add("Email", "This person already exists");
}

И тогда вы можете использовать метод расширения в контроллере для сопоставления этих ошибок с ModelState, например:

public static class ModelStateDictionaryExtensions
{
    public static void Merge(this ModelStateDictionary modelState, IDictionary<string, string> dictionary, string prefix)
    {
        foreach (var item in dictionary)
        {
            modelState.AddModelError((string.IsNullOrEmpty(prefix) ? "" : (prefix + ".")) + item.Key, item.Value);
        }
    }
}

Затем ваш контроллер будет использовать:

ModelState.Merge(personService.ValidatePerson(person), "");

Ответ 2

В качестве альтернативы созданию промежуточного словаря, предложенного Ian, вы также можете сделать это с помощью валидатора, который принимает функцию.

например. в сервисном слое:

public void ValidateModel(Customer customer, Action<string, string> AddModelError)
{
  if (customer.Email == null) AddModelError("Email", "Hey you forgot your email address.");
}

Затем в вашем контроллере вы проверяете один вызов:

myService.ValidateModel(model, ModelState.AddModelError);

Или скажите, что вы хотите использовать свой валидатор в консольном приложении без доступа к ModelStateDictionary, вы можете сделать это:

errors = new NameValueDictionary();
myService.ValidateModel(model, errors.Add);

Оба эти работают, потому что ModelStateDictionary.AddModelError() и NameValueDictionary.Add() соответствует сигнатуре метода для Action<string, string>.