1

在合同方法在服务上完成的非常特殊的情况下,我在 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()
4

1 回答 1

1

使用 KnownType 属性装饰 BaseItem 并将 DerivedItem 指定为属性中的已知类型应该可以解决问题。

例如 - 代码:

[DataContract]
[KnownType(typeof(DerivedItem))]
public class BaseItem { }

摘自 MSDN:“KnownTypeAttribute 类允许您提前指定在反序列化过程中应考虑的类型。有关工作示例,请参阅已知类型示例。”

更多信息请访问:http: //msdn.microsoft.com/en-us/library/ms730167.aspx

于 2013-04-03T08:03:03.157 回答