Дизайн библиотеки классов CRUD для возврата полезных сообщений о сбое бизнес-логики, что не является исключительным

Я создаю базовую библиотеку CRUD, которую я ожидаю использовать как в локальных (добавить ссылку), так и в WCF (добавить ссылку на службу) средах.

каковы наилучшие типы возврата для частей Create, Uupdate и Delete (которые имеют более сложные бизнес-правила) установки CRUD?

Я хочу иметь возможность минимизировать взад и вперед по проводу, но я также хочу предоставить своим клиентам значимую информацию о том, когда операция терпит неудачу в моей бизнес-логике, но технически допустимо (таким образом, это не исключение).

возьмите, например, CRUD для класса Person, который имеет следующие поля: FirstName, MiddleName LastName и Date of Brith. Во-первых, последний, и DOB необходимы, но средний нет.

Как я должен передать сбои бизнес-логики обратно клиенту? Т. е. " вы должны указать значение для FirstName."

  1. Я должна бросать исключения? (это не похоже на исключительный случай, поэтому я думать нет, но я могу ошибаться).
  2. должен ли я использовать void и параметр "out"? Если да, то какого типа?
  3. должен ли я использовать тип возврата объекта и помещать туда данные о том, что происходит?
  4. какой-то другой вариант, который я полностью пропустила?

6 ответов


1.Это здесь я должен делать исключения? (это не похоже на исключительный случай, поэтому я думаю, что нет, но я могу ошибаться).

лично я считаю, что вы должны вернуть объект с результатом, а также любые ошибки проверки, а не создавать исключение для проверки данных, будь то из-за отсутствия информации (проверка формата) или проверка бизнес-логики. Тем не менее, я предлагаю создать исключение для ошибок, которые не связаны с сами данные-т. е.: если фиксация базы данных не выполняется с допустимыми данными и т. д.

Я думаю, что провал проверки не является "исключительным случаем". Я лично чувствую, что все, что пользователь может испортить, просто не вводя достаточно/правильных/etc данных, - это то, что не является исключительным-это стандартная практика и должно обрабатываться API напрямую.

вещи, которые не связаны с тем, что делает пользователь (т. е. сетевые проблемы, проблемы с сервером и т. д.) исключительные случаи и заслуживают исключения.

2.Должен ли я использовать void и параметр "out"? Если да, то какого типа?

3.Должен ли я использовать тип возврата объекта и помещать туда данные о том, что происходит?

Я лично предпочитаю третий вариант. параметры " out " не очень значимы. Кроме того, вы захотите вернуть более одной информации о статусе из этого вызова - вы захотите вернуть достаточно информации для отметьте соответствующие свойства как недопустимые, а также любую полную информацию для всей операции.

для этого, вероятно, потребуется класс, который содержит, как минимум, статус фиксации (success / failed format / failed business logic / etc), список сопоставлений для свойств->errors (ie: IDataErrorInfo style information) и потенциально список ошибок, которые не привязаны к определенному свойству, а касаются бизнес-логики операции в целом или сочетание предложенных значений свойств.

4.Какой-то другой вариант, который я полностью пропустила?

другой вариант, который мне нравится совсем немного, должен иметь проверку в отдельной сборке от уровня бизнес-обработки. Это позволяет повторно использовать логику проверки на стороне клиента.

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

сервер может получать хорошие данные и повторно проверять их и возвращать только один результат фиксации. Я считаю, что это должно иметь по крайней мере три ответа - успех, сбой из-за бизнес-логики или сбой из-за форматирования. Это дает безопасность (вам не нужно доверять клиенту) и дает клиенту информацию о том, что не обрабатывается должным образом, но избегает передачи как плохой информации от клиент- > сервер и информация о проверке от сервера - >клиент, поэтому может резко сократить трафик.

затем слой проверки может (безопасно) отправить информацию в слой CRUD для отправки.


по соображениям производительности первоначальная проверка ввода должна выполняться на уровне клиента. (т. е.: не беспокойтесь о отправке плохих данных по проводу.) На клиентском уровне сбой проверки ввода фактически будет очень ожидаемой проблемой, которая не должна создавать исключения. Однако если вы поместите эту "раннюю" проверку на уровень клиента, сбой проверки данных, обнаруженный на любом более глубоком уровне, можно разумно рассматривать как непредвиденную проблему, поэтому создание исключений для данных ошибки проверки на этих уровнях не будут неуместными.


вы можете найти это сообщение в блоге Роба Бэгби интересным; он описывает, как реализовать репозиторий для обработки операций CRUD, и, на ваш взгляд, в частности, как реализовать проверку, возвращая коллекцию "RuleViolation" клиенту в случае возникновения проблемы.

http://www.robbagby.com/silverlight/patterns-based-silverlight-development-part-ii-repository-and-validation/

[edit] для меня это случай для создания исключения: если для создания пользователя требуется имя, а имя не указано, вызывающий не использовал правильные аргументы и не использует метод по назначению. InvalidArgumentException звучит адекватно.


вы действительно должны проверить реализацию правил проверки Rocky Lhotka в его CSLA framework.

Примечание: Я не скажем, использовать его фреймворк в массовом порядке, так как у него есть некоторые проблемы с соединением, которые нарушают некоторые усилия SRP в последних тенденциях развития .NET.

но его структура использует "автоматическое" уведомление до уровня пользовательского интерфейса и интеграцию с сообщениями об ошибках проверки с поддержкой Элементы управления Web/Winforms.


Edit: я должен отметить, что обычно у меня есть материал проверки, абстрагированный в бизнес-слой, который будет обрабатывать проверку и вызывать методы CRUD после успешной проверки.

один из способов сделать это - передать класс" response", который содержит всю информацию, необходимую потребителю вашей библиотеки для оценки того, что произошло и что делать дальше. Очень простой пример класса, который вы могли бы использовать что-то вроде бы это:

public class Response<T> where T:BusinessObject
{
    public Response(T oldOriginalValue, T newValue)
    {
    }

    /// <summary>
    /// List of Validation Messages
    /// </summary>
    public List<ValidationMessage> ValidationMessages { get; set; }

    /// <summary>
    /// Object passed into the CRUD method
    /// </summary>
    public T OldValue { get; private set; }

    /// <summary>
    /// Object passed back from the CRUD method, with values of identity fields, etc populated
    /// </summary>
    public T NewValue { get; private set; }

    /// <summary>
    /// Was the operation successful
    /// </summary>
    public bool Success { get; set; }
}

public class ValidationMessage
{
    /// <summary>
    /// Property causing validation message (i.e. FirstName)
    /// </summary>
    string Property { get; set; }

    /// <summary>
    /// Validation Message text
    /// </summary>
    string Message { get; set; }
}

дебаты между возвращением структуры результата с деталями сбоя и исключением могут быть обобщены на "Могу ли я успешно выполнить запрошенное действие?"Вы разрабатываете библиотеку, и то, как библиотека передает режимы сбоя, является частью этого.

Если библиотеке предлагается создать объект пользователя, но не может, потому что имя пользователя не удалось проверить, вы можете передать обратно пустой пользователь вместе с сообщениями об ошибке проверки и надеяться, что клиентский код проверяет возвращаемое значение. Мой опыт (DCOM pre ATL flashbacks) с необходимостью тестирования возвращаемых значений для кодов ошибок заключается в том, что легко успокоиться (или лениво) и пропустить его.

из того, что вы описали, использование исключений не исключено. Определите родительское исключение для всех типов исключений, которые может создать библиотека. Таким образом, клиенты не должны включать большой список суб-исключений, если они действительно этого не хотят. Примером этого является SqlException.