在合同方法在服务上完成的非常特殊的情况下,我在 WCF 客户端上收到异常 - 即异常发生在服务返回之后,客户端代理返回之前。
我已经确认这个问题与返回类型是给定类型的数组有关,而该数组包含派生类型的实例。对于非 WCF 使用,返回此数组完全没有例外(如代码所示),并且当从客户端调用该方法时,它会在服务端返回。
现在,从我可以捕获的方法中抛出的异常,或者如果我未能捕获,则接收客户端,但是这种问题显然被悄悄地吞下了,唯一可以从客户端异常收集的信息是套接字错误连接关闭后!
如果我们挂钩 Closing/Faulted 事件,我们确实会在服务端看到一个 Fault & Close,然后在异常之前在客户端看到一个 Fault。
遵循下面的最小案例,并注意我正在尝试多种方法来获取更多信息。注意:我省略了实际ServiceContract
,因为它从实现中足够清楚(只有一种方法)。
public class BaseItem {}
public class DerivedItem : BaseItem {}
// Service implementation plus fault & error handling
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true, UseSynchronizationContext = false)]
public class BasicService : IMyService, IErrorHandler {
BaseItem[] IMyService.GetItems(bool useDerived) {
return new BaseItem[] { useDerived ? new DerivedItem() : new BaseItem() };
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {}
public bool HandleError(Exception error) { return false; }
}
public class ClientLauncher {
public static void LaunchClient() {
IMyService service = new BasicService();
BaseItem[] items = service.GetItems(false); // Works fine
items = service.GetItems(true); // Works fine
ServiceHost host = new ServiceHost(service); // Singleton instance
host.AddServiceEndpoint(typeof(IMyService), new NetTcpBinding(),
"net.tcp://localhost:8000");
host.Open();
// Adding service as the implementation of IErrorHandler
((ChannelDispatcher)host.ChannelDispatchers[0]).ErrorHandlers.Add((IErrorHandler)service);
var myFactory = new ChannelFactory<IMyService>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8000"));
IMyService client = myFactory.CreateChannel();
try {
items = client.GetItems(false); // Works fine
items = client.GetItems(true); // Results in channel faulted, then exception
}
catch (Exception ex) {
string str = ex.ToString();
}
}
}
直接调用可以GetItems
正常工作,并且客户端调用GetItems
可以工作,但前提是数组没有派生类型的实例。该数组是必需的 -DerivedItem
如果方法返回 single ,则返回 a 没有问题BaseItem
。
但同样,真正的问题更为普遍——不管具体的挑衅性问题是什么,当 1. 异常似乎仅表明套接字已被服务关闭和 2. IErrorHandler 时,我们如何才能找到有关它的任何信息补充,是不是叫?我真的很想使用 WCF,但如果这是所有例外都会告诉我的话,我的信心会非常动摇:
************** Exception Text **************
System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:59.9090000'. ---> System.IO.IOException: The read operation failed, see inner exception. ---> System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:59.9090000'. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.ServiceModel.Channels.SocketConnection.ReadCore(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout, Boolean closing)
--- End of inner exception stack trace ---
at System.ServiceModel.Channels.SocketConnection.ReadCore(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout, Boolean closing)
at System.ServiceModel.Channels.SocketConnection.Read(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout)
at System.ServiceModel.Channels.DelegatingConnection.Read(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout)
at System.ServiceModel.Channels.ConnectionStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.Security.NegotiateStream.StartFrameHeader(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.NegotiateStream.ProcessRead(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
--- End of inner exception stack trace ---
at System.Net.Security.NegotiateStream.ProcessRead(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.NegotiateStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.ServiceModel.Channels.StreamConnection.Read(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout)
--- End of inner exception stack trace ---
Server stack trace:
at System.ServiceModel.Channels.StreamConnection.Read(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout)
at System.ServiceModel.Channels.SessionConnectionReader.Receive(TimeSpan timeout)
at System.ServiceModel.Channels.SynchronizedMessageSource.Receive(TimeSpan timeout)
at System.ServiceModel.Channels.FramingDuplexSessionChannel.Receive(TimeSpan timeout)
at System.ServiceModel.Channels.FramingDuplexSessionChannel.TryReceive(TimeSpan timeout, Message& message)
at System.ServiceModel.Dispatcher.DuplexChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at KapLogic.Aegis.Drivers.P2000.Common.WCF.IMyService.GetItems(Boolean useDerived)
at KapLogic.Aegis.Drivers.P2000.Common.WCF.ClientLauncher.LaunchClient()