【问题标题】:exceptions and coupling异常和耦合
【发布时间】:2012-12-20 07:32:48
【问题描述】:

我有这个接收队列消息的主类,然后使用其他一些类来做一些工作。所有这些其他类本身都使用一些较低的类,最终将数据写入数据库或发送到 wcf 服务。

根据低级类的结果,主类必须决定是删除队列消息,还是将其重新放入队列,或者将其发送到死信队列。

例如,如果数据库无法访问,则可以将 queuemessage 放入队列中稍后再试。但是如果 wdcf 服务返回它不接受某些数据,则必须将消息发送到死信队列。

我有几种方法来实现这个场景:

  1. 抛出异常并仅在主类中处理它们。
  2. 抛出异常,但在每个调用类中捕获它们。并重新抛出一个新异常
  3. 返回指示错误/成功状态的结果对象

这些是我对场景的想法:

  1. 如果最低类之一抛出异常,并且主类必须处理它,它会将主类一直耦合到最低类。如果最低类之一决定更改异常,我必须更改主类异常处理。

  2. 没有好办法让上层类知道C#中被调用的类会抛出哪些异常。

  3. 这是我更喜欢的。每个被调用的方法都可以返回一个结果对象,带有一个指示成功或失败的枚举,以及失败的类型。

所以,我的首选方式是选项 3,但我不知道这在架构上是否可以接受。或者是否有更好的方法。

代码

这就是代码(简化形式)的样子:

队列处理程序

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 服务调用失败、文件不存在等。

我希望我能阻止这种情况:

队列处理程序

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

【问题讨论】:

  • 到目前为止你有什么关于你可以展示的代码以便有人可以提供更好的方法..如果你发布这种性质的代码..它可能最好在 codereview 中提供关于stackoverflow的部分。

标签: c# exception coupling


【解决方案1】:
  1. ...它将主类一直耦合到最低类。

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

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

异常被认为是superioroption,因为您不必检查来确定是否发生了错误。

【讨论】:

  • 对不起,如果我误解了你,但基本上在大规模层面上,你建议使用Parse() 而不是TryParse(),用较低的.net 术语。
  • @IlyaIvanov - 你在说什么?您的评论似乎与这里的问答无关。
  • 我问过同样的问题,正如您在链接中给出的那样:“异常或错误代码”。一种方法是依靠返回值,另一种方法是依靠方法抛出的异常。
  • @Ilya - TryParse 是一个非常具体的模式。它往往与 UI 相关(在验证用户输入时),通常在较低级别的代码中找不到。这实际上取决于使用场景 - 如果我想告诉用户他们的输入不正确,我会使用 TryParse
猜你喜欢
  • 2017-05-09
  • 1970-01-01
  • 2010-09-07
  • 1970-01-01
  • 2013-12-25
  • 2017-10-12
  • 1970-01-01
  • 2021-06-05
相关资源
最近更新 更多