【问题标题】:How to Return DTO Nicely Without Closing Connection in WCF如何在不关闭 WCF 连接的情况下很好地返回 DTO
【发布时间】:2012-01-13 00:34:58
【问题描述】:

我的 C# 中的 WCF 服务如下所示。

[ServiceContract]
public class MySecretService
{
      [OperationContract]
      [FaultContract(typeof(ErrorMessage))]
      public MyDTO ReturnMyDTOMethod(int id, out string errorMessage)
      {
        try
        {
           //Do stuff...

           //Do some more stuff... pseudocode
          if (biz rule 1 && etc)
          {
            return MyDTO;
          }
          else if (biz rule 2 && etc)
          {
            return null;
          }
          else
          {
            throw new FaultException<ErrorMessage>(blah etc..)
          }
        }
        catch (Exception e) 
        {
            throw new FaultException<ErrorMessage>(blah etc...);
        }
     }//end-method
}//end-class

要从该方法返回的 DTO 如下所示:

[DataContract]
public class MyDTO
{
  [DataMember]
  public XElement XmlRep
  {
    get
    {
        //do something within setter, etc...

    //error occurs prior to returnin from setter, where to i catch it?
    return _xmlRep
    }
    set
    {
    _xmlRep = value;

    }

  }
}

我发现的典型示例显示了从方法中抛出 FaultException;但就我而言,我的方法没有错误;错误发生在对象被返回给客户/消费者的时候;即当 DataMember/Property XmlRep 被序列化时;

所以我不能将 throw FaultException 放在我的方法中;但我仍然想避免得到 “底层连接已关闭:连接意外关闭。”并抛出发生在 getter 中的正确错误。

我没有尝试将 try/catch 放在 MyDTO 的 getter 中,我也不想因为我希望我的 DTO 尽可能简单,并且对 FaultExceptions 和 WCF 的东西一无所知。还有其他想法吗?

编辑:为了更清楚,我知道错误发生在 MyDto DataContract 的 Getter 中;但是我还能在哪里抛出 FaultException,因为在我看来,在 Getter 内部是一个不可靠的地方来抛出它?

EDIT#2:我在服务端实现了一个包罗万象的错误处理程序,正如 Tim 下面所建议的那样(使用 IErrorHandler);这在我的具体情况下不起作用。我认为这是因为错误不会发生在 OperationContract ReturnMyDTOMethod() 中,而是在被序列化时发生在 MyDto 中;换句话说,看起来马已经跑了(方法成功返回),并且 IErrorHandler 有任何用途都为时已晚 - 具体来说,ProvideFault() 不会触发,但 HandleError() 会触发。因此,我仍然收到一条频道中断消息,这需要我回到绘图板 - 即确保 MyDto 不会做任何花哨的事情,例如产生错误!

【问题讨论】:

  • 不要随便提到你得到一个错误,就好像它是无关紧要的,请完整发布你的异常!
  • @hugh,请重新阅读问题;我认为很清楚,错误是“底层连接已关闭:连接意外关闭。”此错误出现在消费者/客户端。
  • @joedotnot - 您可以通过 IErrorHandler 的实现(下面我的答案中的选项 2)捕获异常并从那里抛出 FaultException,或者当您实例化 DTO 和扔掉它。

标签: wcf channel faultexception


【解决方案1】:

我看到了两个选项:

  1. 听起来您的 DataContract 中的某些内容无法序列化。我会看看那里并确定那是什么,为什么它没有被序列化,以及是否有其他方法可以在 setter 中处理问题。您可以在没有 try-catch 块的情况下执行此操作,具体取决于它是什么。在没有看到代码或不知道实际错误是什么的情况下,很难给出更具体的内容。

  2. 在您的服务中实现IErrorHandler 接口。这将捕获任何未处理的异常,您可以根据服务需要处理它们(例如,通过 FaultException 序列化错误消息)。网上有很多关于如何做到这一点的示例 - 只需 google IErrorHandler 和 WCF。

我个人的偏好是尝试方法 #1 - 您的 DTO 没有序列化可能有一个特定原因(或一组原因),我觉得最好在代码中处理这些问题而不是依赖某种类型全局错误处理程序(这就是#2)。

此外,根据您的 setter 中的逻辑,您可能已经使 DTO 变得复杂。我通常会尽量避免在我的 setter 中使用任何逻辑,除非它是像 null 检查这样简单的东西。

在问题的第二次编辑后添加

IErrorHandler 的文档说明:

“在调用所有ProvideFault实现并将响应消息传递给通道后,可能会发生异常。如果发生通道异常(例如,难以序列化消息),则不会通知IErrorHandler对象。在这种情况下,您应该确保您的开发环境捕获并向您显示此类异常,或者使用跟踪来发现问题。” (强调我的)

你知道导致序列化失败的原因是什么吗?如果你这样做,我认为你可以在代码本身中解决它。您是否启用了跟踪?在我看来,您需要确定您的 DTO 无法序列化的原因。

【讨论】:

  • 感谢蒂姆,关于 DataContract(即 MyDto 对象)无法序列化的说法是正确的;事实上,我确切地知道错误是什么,并且它发生在 XmlRep getter 属性中,这是对已知问题的陈述。真正的问题是我将如何在消费者或客户端很好地捕捉到它,也就是说,而不是接收通用的 Wcf 频道损坏消息“底层连接已关闭..等等等等”.. 我想抛出一个很好的消息客户端可以接收。任何进一步的意见表示赞赏。
  • 如果您无法修复 DTO,使其在序列化时不会中断,则使用选项 2(使用 IErrorHandler 实现全局错误处理)或让您的服务填充/填充 DTO尝试捕获块。然后,您可以返回用户友好的 FaultException 消息。
  • 蒂姆,你似乎是说如果我选择选项 2(全局错误处理程序),我不能使用选项 1? (即在方法/OperationContract 级别抛出本地化的 FaultException 毫无意义,因为它们无论如何都会通过全局错误处理程序?请确认。
  • @joedotnot - 您可以使用一个或两个选项。选项 1 的目标是通过使代码健壮来避免一起抛出异常。选项 2 背后的意图(至少在我使用它时)是为了捕获服务抛出的任何在服务代码中处理的异常。服务中未处理的异常将导致通道故障;实现全局错误处理程序为您提供了一种方法,既可以防止通道故障,又可以向客户端显示一条好消息。
  • Tim,使用 IErrorHandler 的选项 2 在我的具体示例中不起作用。请在问题末尾查看我上面的第二次编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-15
  • 2016-05-18
  • 2014-02-18
  • 2011-09-01
  • 2022-01-15
  • 2019-03-21
相关资源
最近更新 更多