Какой тип результата должен быть возвращен из сервисного уровня? - PullRequest
7 голосов
/ 31 декабря 2010

Скажем, например, что у меня есть PostsService, который создает сообщение:

public class PostsService : IPostsService
{
    public bool Create(Post post)
    {
        if(!this.Validate(post))
        {
            return false;
        }

        try
        {
            this.repository.Add(post);
            this.repository.Save();
        }
        catch(Exception e)
        {
            return false;
        }
    }
}

Проблема в том, что если во время действий с репозиторием возникает исключение, оно проглатывается.Create() возвращает false, и все, что потребитель знает, это то, что Post не был добавлен, но не знает , почему .

Вместо этого я думал о том, чтобы иметь класс ServiceResult:

public class ServiceResult
{
    public bool Success { get; private set; }
    public Exception Exception { get; private set; }
}

Это было бы хорошим дизайном?Или мне даже нужно сообщить об этих ошибках потребителю?Достаточно ли сказать «Произошла ошибка при добавлении поста».а затем зарегистрировать исключение внутри службы?

Любые другие предложения приветствуются.

Ответы [ 6 ]

4 голосов
/ 31 декабря 2010

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

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

Если с другой стороны есть какая-то внутренняя проблема с БД, потребитель не должен быть проинформирован о деталях (потому что это исключительная ответственность службы; и потребитель ничего не может с этим поделать).

Учитывая это, я бы сказал, что возвращение логического значения во многих случаях недостаточно. Так что наличие какого-то типа ServiceResult не кажется плохой идеей. Я не уверен, что включил бы Exception, хотя. Но, возможно, вы можете создать какую-то ServiceExpection, специфичную для вашего сервиса. Иногда для этого тоже подходят коды ошибок.

2 голосов
/ 31 декабря 2010

Да, класс ServiceResult - хорошая идея, но я бы не стал включать исключение, бывают случаи, когда вы хотите сообщить об ошибках, но вам не нужно / не нужно создавать / бросать / перехватывать исключение, вместо этогоЯ бы включил коллекцию сообщений об ошибках или тип enum, указывающий, что пошло не так, что-то вроде MembershipCreateStatus .

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

1 голос
/ 01 января 2011

Это действительно плохая идея, чтобы поймать все исключения. Вы ловите только то, с чем можете разумно справиться. Вы не можете обработать все исключения, так почему вы ловите это? Чтобы предотвратить трассировку стека?

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

Допустим, что служба вызвала исключение OutOfMemoryException, вы только что его перехватили и не будете знать, утечка памяти у вашего приложения, как сито, или неправильное распределение больших объектов.

Это плохо.

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

catch (Exception e)
{
    //send it to yourself, log it. Just don't swallow it.
    LogErrorAndEmailDevTeam(e); 
    throw new SaveFailedException("We're sorry. \n" + 
    "It's not your fault, but something bad happened.\n\n" + 
    "We've been notified and will fix it as soon as possible.");
}

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

Вы не хотите, чтобы ваше приложение продолжало работать в поврежденном состоянии, но именно это и делает catch (Exception). Вы действительно уверены, что справитесь с тем, что бросили?

1 голос
/ 31 декабря 2010

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

Должен ли вызывающий слой действительно знать, почему произошел сбой службы? Что это может поделать? Это просто, чтобы вы могли показать лучшее сообщение для пользователя? В этом случае пользователь действительно заботится о деталях? Легко думать, что пользователь хочет знать больше, просто потому, что вы, как разработчик, знаете. Но разработчики должны получать информацию об сообщениях об ошибках из журналов, а не из сообщений конечных пользователей.

1 голос
/ 31 декабря 2010

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

Все больше и больше в моих проектах услуг я перешел к системе запросов / ответов.(На самом деле, я интенсивно использовал библиотеку agatha-rrsl в этом отношении.) В этом проекте у меня есть объект BaseRequest и объект BaseResponse, из которого все запросы и ответытипы наследуют.

Продолжая то, о чем вы думали, этот класс ServiceResult послужил бы началом такого BaseResponse.Это не обязательно нуждается в действительном исключении, но я склоняюсь к нескольким вещам:

  1. A bool, указывающий на успех или неудачу для любого запроса, даже если он возвращает фактические результаты в случае успеха.
  2. IList<> пользовательских Message объектов, которые сами содержат какой-либо тип сообщения (Info, Warn, Error и т. Д.), А также текст сообщения (и, возможно, дополнительный удобный для пользователятекст сообщения для отображения интерфейса пользователя), трассировка стека, если это необходимо, и т. д.

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

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

0 голосов
/ 31 декабря 2010

Если вы используете WCF, я бы рекомендовал использовать ServiceResult или что-то подобное для работы с исключениями в слое SOA.

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

В этом вопросе StackOverflow полностью реализованы контракты на отказ .

Вы могли бы по-прежнему говорить своему внешнему интерфейсу просто «Произошла ошибка», но использование контракта на отказ позволит лучше обрабатывать ошибки на уровне пользовательского интерфейса вашего кода, если в будущем это станет необходимым.

...