Исключения и связь

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

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

Если, например, база данных недоступна, сообщение очереди может быть помещено в очередь, чтобы повторить попытку позже. Но если служба wdcf возвращает, что она не принимает некоторые данные, сообщение должно быть отправлено в очередь deadletter.

У меня есть несколько способов реализовать этот сценарий:

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

Вот мои идеи о сценариях:

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

  2. Нет хорошего способа сообщить старшим классам, какие исключения будут выброшены из вызываемого класса в C#.

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

Таким образом, я предпочитаю вариант 3, но я не знаю, приемлемо ли это архитектурно. Или, если есть какие-то лучшие способы.

Код

Вот как выглядит код (в упрощенном виде):

QueueHandler

private static void HandleQueueMessage(Message message)
{
    var deliveryOrder = deserialize(message.body);

    var deliveryOrderHandler = new DeliveryOrderHandler();

    var result = deliveryOrderHandler.Execute(deliveryOrder.PubId);

    switch (result)
    {
        case DeliveryOrderHandlerResult.DeliverySucceeded:
            break;

        case DeliveryOrderHandlerResult.FataleErrorInExternalSystem:
        case DeliveryOrderHandlerResult.MAndatoryDocuhmentTransformationFailed:
            SendDeliveryOrderToDeadletterQueue(deliveryOrder);
            break;

        default:
            deliveryOrder.AbortCount = deliveryOrder.AbortCount + 1;
            ResendDeliveryOrderToQueue(deliveryOrder);
            break;
    }
}

DeliveryOrderHandler

private DeliveryOrderHandlerResult Execute(long pubId)
{
    DeliveryOrderHandlerResult deliveryOrderHandlerResult;

    var transformationResult = GetTransformationResultaat(pubId);

    if (transformationResult == TransformationResult.Success)
    {
        var deliveryResult = DeliverDocumentToExternalSystem(pubId); 

        if (deliveryResult.Status == DeliveryResult.Success)
        {
            SaveDeliveryResult(pubId, deliveryResult);
        }

        deliveryOrderHandlerResult = deliveryResult.Status;
    }
    else
    {
        switch (transformationResult)
        {
            case TransformationResult.NotStarted:
                deliveryOrderHandlerResult = DeliveryOrderHandlerResult.TransformationNotStarted;

            case TransformationResult.Busy:
                deliveryOrderHandlerResult = DeliveryOrderHandlerResult.TransformationBusy;

            case TransformationResult.MandatoryTransformationFailed:
                deliveryOrderHandlerResult = DeliveryOrderHandlerResult.MandatoryTransformationFailed;

            default:
                throw new Exception(--unknown enum value --);
        }
    }

    return deliveryOrderHandlerResult;
}

DeliverDocumentToExternalSystem

pseudo:
- Create Delivery package by reading data from database and transformed files from disk
- Send package to external system

Как видите, многое может пойти не так; сбой соединения с базой данных, сбой вызовов службы wcf, файлы отсутствуют и т. д.

Я надеялся, что смогу предотвратить это:

QueueHandler

private static void HandleQueueMessage(Message message)
{
    var deliveryOrder = deserialize(message.body);

    var deliveryOrderHandler = new DeliveryOrderHandler();

    try
    {
        var result = deliveryOrderHandler.Execute(deliveryOrder.PubId);

        switch(result)
        {
           case DeliveryOrderHandlerResult.Success:
              // remove message from queue

           case DeliveryOrderHandlerResult.NotStarted:
              // resent message to queue

           case DeliveryOrderHandlerResult.MandatoryTransformationFailed:
              // send message to deadletterqueue

           case ...
              // handle

           case ...
              // handle

        }
    }
    pseudo catches:
    catch (DatabaseNotFoundexception ex)
    {
        // resent message to queue
    }
    catch (ExternalWcfServiceDownException ex)
    {
        // resent message to queue
    }
    catch (FileNotFoundException ex)
    {
         // send message to deadletterqueue
    }
    catch (...)
    {
        // handle
    }
    catch (...)
    {
        // handle
    }
}

1 ответ

  1. ... он соединяет основной класс вплоть до низших классов.

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

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

Исключения считаются лучшим вариантом, так как вам не нужно проверять, не произошла ли ошибка.

Другие вопросы по тегам