0

I have this main-class that receives a queuemessage and then uses a few other classes to do some work. All these other classes use some lower classes themselves, and eventually data is written to a database or send to wcf services.

Based on the result of the lower classes the main-class has to decide wheter to remove the queuemessage, or place it on the queue again, or send it to a deadletterqueue.

If for example a the database is unreachable, the queuemessage can be placed in the queue to try it again later. But if the wdcf service returns that it doesn't accept some data, the message has to be sent to the deadletterqueue.

I have a couple of ways to implement this scenario:

  1. Throw exceptions and only handle them in the main-class.
  2. Throw exceptions but catch them in each calling class. And rethrow a new exception
  3. Return result-objects which indicate error/success-state

These are my ideas about the scenarios:

  1. If one of the lowest classes throws an exception, and the main-class has to handle it, it couples the main-class all the way to the lowest classes. If one of the lowest classes decides to change an exception, I have to change the main-class exception handling.

  2. There is no good way to let upper classes know which exceptions will be thrown from the called class in C#.

  3. This is what i prefer. Every called method can return a result-object, with an enum indicating succes or failure, and the type of failure.

So, my preferred way is option 3, but I don't know if that's architecturally acceptable. Or if there are any better ways.

Code

This is what the code (in simplified form) looks like:

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

As you can see there's a lot that can go wrong; failed database connections, failed wcf service calls, files not present etc.

I was hoping I could prevent this:

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
    }
}
4

1 回答 1

1
  1. ...它将主班一直耦合到最低班。

不,它将您的主类与异常类型结合起来。是的,如果您将异常更改为较低,则需要将处理更改为较高,但耦合是在异常级别上。

3 不是一个好的选择,因为您可能会忘记检查退货。您最终会遇到与选项 1 中列出的完全相同的问题,就好像您将结果对象更改得很低,您需要在主类中进行更改......

异常被认为是更好的 选择,因为您不必检查是否发生错误。

于 2013-01-06T10:33:00.757 回答