WCF中如何在不关闭连接的情况下很好地返回DTO
我的 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;
}
}
}
我发现的典型示例显示从该方法内抛出错误异常;但就我而言,我的方法没有错误;错误发生在对象返回给客户端/消费者时;即当 DataMember/Property XmlRep 被序列化时;
所以我不能在我的方法中放置抛出FaultException;但我还是想避免 “底层连接已关闭:连接意外关闭。”并抛出 getter 中发生的正确错误。
我没有尝试将 try/catch 放入 MyDTO 的 getter 中,我也不想这样做,因为我希望我的 DTO 尽可能简单,并且对FaultExceptions 和 WCF 的东西一无所知。还有其他想法吗?
编辑:为了更清楚地说明,我知道错误发生在 MyDto DataContract 的 Getter 中;但是我还能在哪里抛出FaultException,因为在我看来,在Getter 内部似乎是一个危险的地方?
编辑#2:我在服务端实现了一个包罗万象的错误处理程序,如下面蒂姆建议的那样(使用 IErrorHandler);这在我的具体情况下不起作用。我认为这是因为错误不是发生在OperationContract ReturnMyDTOMethod()中,而是发生在序列化时的MyDto中;换句话说,看起来马已经逃跑了(方法成功返回),而 IErrorHandler 没有任何用处——具体来说,ProvideFault() 不会触发,但 HandleError() 会触发。因此,我仍然收到一条通道损坏的消息,这需要我回到绘图板 - 即确保 MyDto 不会执行任何花哨的操作,例如生成错误!
My WCF Service in C# looks like this.
[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
The DTO to be returned from the method looks like this:
[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;
}
}
}
The typical examples that i found shows throwing a FaultException from within the method; But in my case, my method does not error; The error occurs at the time when the object is being returned to the client/consumer; i.e when the DataMember/Property XmlRep is being serialized;
So i cannot place the throw FaultException within my method; But i still want to avoid getting
"The underlying connection was closed: The connection was closed unexpectedly." and throw the proper error that occurs within the getter.
I have not tried putting try/catch inside the getter of MyDTO, NOR DO I WANT TO BECAUSE i want my DTO to be as simple as possible, and know nothing about FaultExceptions and WCF stuff. Any other ideas ?
EDIT: Just to make it clearer, i know the error is occurring in the Getter of MyDto DataContract; But where else would i throw the FaultException, given that inside the Getter seems to me like a dodgy place to throw it?
EDIT#2: I implemented a catch-all error handler on the service side as suggested by Tim below (using IErrorHandler); this does not work in my specific case. I think this is because the Error does not occur within the OperationContract ReturnMyDTOMethod(), but instead within the MyDto when is being serialized; In other words, it appears the horse has bolted (method returns successfully), and it is to late for the IErrorHandler to be of any use - Specifically ProvideFault() does not fire but HandleError() does fire. Consequently i still get a channel broken message, which requires me to go back to the drawing board - i.e. ensure MyDto does not do anything fancy such as generate an error!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我看到两个选项:
听起来您的 DataContract 中的某些内容无法序列化。我会看一下那里并确定那是什么,为什么它没有被序列化,以及是否有其他方法可以在设置器中处理该问题。您也许可以在没有 try-catch 块的情况下做到这一点,具体取决于它是什么。在没有看到代码或不知道实际错误是什么的情况下,很难提供更具体的信息。
在您的服务中实现 IErrorHandler 接口。这将捕获任何未处理的异常,并且您可以根据服务需要来处理它们(例如,通过FaultException序列化错误消息)。网络上有很多关于如何执行此操作的示例 - 只需 google IErrorHandler 和 WCF。
我个人的偏好是尝试方法 #1 - 可能有一个特定的原因(或一组原因)您的 DTO 没有序列化,我觉得最好在代码中处理这些问题,而不是依赖某种全局错误处理程序(这就是#2)。
另外,根据 setter 中的逻辑,您可能已经使 DTO 变得复杂。我通常会尝试避免设置器中的任何逻辑,除非它是像空检查这样简单的东西。
在问题中的第二次编辑后添加
IErrorHandler 的文档指出:
“调用所有 ProvideFault 实现并将响应消息传递给通道后,可能会发生异常。如果发生通道异常(例如,难以序列化消息) IErrorHandler 对象不会被通知。在这种情况下,您应该确保您的开发环境捕获并向您显示此类异常,或者利用跟踪来发现问题。” (强调我的)
你知道是什么导致序列化失败吗?如果您这样做,我认为您可以在代码本身中解决它。您启用了跟踪吗?在我看来,您需要确定为什么您的 DTO 无法序列化。
Two options I see:
It sounds like something in your DataContract isn't able to be serialized. I would take a look there and determine what that is, why it isn't being serialized, and if there is some other way you can handle the issue within the setter. You may be able to do that without a try-catch block, depending on what it is. Without seeing the code or knowing what the actual error is, it's hard to give anything more specific.
Implement IErrorHandler Interface in your service. This will catch any unhandled exceptions and you to handle them as your service needs (for example, serializing an error message via FaultException). There are plenty of examples on the web of how to do that - simply google IErrorHandler and WCF.
My personal preference would be to try approach #1 - there's probably a specific reason (or group of reasons) your DTO isn't serializing, and I feel it's better to handle those issues within the code rather than relying on some sort of global error handler (which is what #2 would be).
Also, depending on the logic in your setter, you may have already made your DTO complex. I generally try to avoid any logic in my setter unless it's something simple like a null check.
Added After 2nd Edit in Question
The documentation for IErrorHandler states:
"Exceptions can occur after all ProvideFault implementations are called and a response message is handed to the channel. If a channel exception occurs (for example, difficulty serializing the message) IErrorHandler objects are not notified. In this case, you should make sure that your development environment catches and displays such exceptions to you or makes use of tracing to discover the problem." (Emphasis mine)
Do you know what is causing the serialization to fail? If you do, I would think you could address it in the code itself. Have you enabled tracing? It seems to me that you need to determine why your DTO can't serialize.