Проверка бизнес-логики в Asp.net MVC с использованием исключений - PullRequest
6 голосов
/ 28 октября 2009

У меня есть вопрос относительно метода, который я использую для проверки бизнес-правил в asp.net mvc.

В настоящее время у меня есть класс исключений, который выглядит примерно так

public class ValidationException : Exception 
{
    private ModelStateDictionary State { get; set; }
    public ValidationException(ModelStateDictionary state)
    {
        State = state;
    }
    public void MergeModelStates(ModelStateDictionary state)
    {
        state.Merge(this.State);
    }
}

и валидатор, который выглядит так

public void Validate(IEntity entity)
{
    ModelStateDictionary state = new ModelStateDictionary();
    if (entity.Contact != null && _service.GetBy(entity.Contact.Id) == null)
        state.AddModelError("Contact", "Invalid Contact.");
    if (entity.Title.Length > 8)
        state.AddModelError("title", "Title is too long...");
    ... etc
    if (!state.IsValid)
        throw new ValidationException(state);
}

и контроллер, который делает что-то вроде этого

public ActionResult Add()
{
    var entity = new InputModel;
    try
    {
        TryUpdateMode(inputModel);
        ..... Send input to a Repository (Repository calls Validate(entity);
    }
    catch (ValidationException validationException)
    {
       validationException.MergeModelStates(this.ModelState);
       TryUpdateModel(inputModel);
       return View("Add",inputModel);
    }
    return View("List");
}

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

Спасибо за любую помощь

Ответы [ 2 ]

13 голосов
/ 28 октября 2009

Исключения, как правило, должны быть для исключительных случаев, а не для обработки чего-то, что обычно может происходить во время нормального выполнения вашей программы. Для этого есть много веских причин - вот некоторые, с которыми я довольно часто сталкиваюсь:

  1. Проблемы с производительностью. Исключениями являются, как правило, довольно дорогие операции - если их регулярно генерировать, ваша производительность может пострадать.
  2. Работа с неисследованными исключениями проверки - в случае, если вам случится использовать свой код, не имея дело с исключением, вы увидите ошибку проверки до «желтого экрана» или обработчика сбоя - вероятно, не самый лучший пользовательский опыт.
  3. Исключения не структурированы таким образом, чтобы обеспечить хорошую информацию для пользователя. Взгляните на класс исключений - там не так много настроек, обеспечивающих хорошую информацию для пользователя, то, что вам понадобится для передачи информации обратно пользователю. Каждый раз, когда я пытался использовать исключения таким образом, я получал целый ряд подклассов со свойствами, которые не имеют особого смысла в принадлежности к исключению.

Один из подходов, который мне обычно нравится использовать, - это предоставить открытый метод Validate, который возвращает список ошибок (но никогда не генерирует само исключение), а затем метод Save, который вызывает Validate () и выдает исключение, если есть какие-либо ошибки. Вы переключаете поведение с «throw, если модель недействительна» на «throw, если код пытается сохранить, пока модель находится в недопустимом состоянии».

Чтобы ответить на комментарий ниже относительно производительности бросков в Validate vs. Save - бросок в Save () будет иметь точно такое же снижение производительности как и бросок в Validate (). Однако ключевое отличие состоит в том, что этого никогда не должно происходить - вы защищаетесь от того, что разработчик неправильно использует ваши классы, а не использует исключения в качестве метода проверки. Правильно написанный код, вызывающий метод save, должен выглядеть примерно так:

ValidationResult result = obj.Validate();
if (result.IsValid) {
   obj.Save();
} else {
   // display errors to the user
}

Исключение выдается, только если разработчик забыл проверить состояние проверки перед сохранением. Это имеет то преимущество, что позволяет проводить проверку без использования исключений, а также защищать базу данных, не позволяя сохранять недействительные объекты. В идеале вы бы вообще не уловили исключение в контроллере и позволили бы с ним справиться в общих процедурах обработки ошибок, поскольку проблема больше не в пользовательском вводе, а в ошибке разработчика.

2 голосов
/ 28 октября 2009

В идеале вы должны использовать функцию аннотации данных в ASP.NET MVC 2: -

http://stephenwalther.com/blog/archive/2008/09/10/asp-net-mvc-tip-43-use-data-annotation-validators.aspx

http://weblogs.asp.net/scottgu/archive/2009/07/31/asp-net-mvc-v2-preview-1-released.aspx

Например: -

public class Person
{
  [Required(ErrorMessage="Please enter a name.")]
  public String Name { get; set; }
}

Если вы не готовы к обновлению, есть решение:

Если вы передаете ссылку на словарь ModelState контроллера (оберните его внутри интерфейса, если вас беспокоит разделение проблем) на ваш Validator, вызовите AddModelError, если вы обнаружите ошибки, тогда в вашем контроллере вы можете вызвать if ModelState.IsValid) и примите соответствующие меры: -

var entity = new InputModel();

TryUpdateModel(entity);
MyValidator.Validate(entity, ModelState);

if(ModelState.IsValid) { ...

и ваш валидатор выглядит так: -

public void Validate(IEntity entity, ModelStateDictionary state)
{
  if (entity.Contact != null && _service.ValidId(entity.Contact.Id) == null)
    state.AddModelError("Contact", "Invalid Contact.");
  // etc
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...