SerializationException不会转到IErrorHandler

时间:2013-12-31 21:44:02

标签: c# .net wcf

我在尝试反序列化一个相对较大的对象图时看到奇怪的行为〜上面有6列的10000行。我确定问题是,当尝试将此数组从服务反序列化回客户端时,存在序列化异常。它适用于少于9000个对象的数据集,控制台窗口显示第一次机会SerializationException,后跟CommunicationException。我自己在控制台应用程序中托管此服务,因为我使用它与第三方API集成。我的第一个问题是,有谁知道有什么可能导致这种血清异常?

我在客户端和服务器上以下列方式配置代码绑定,我想我已经把它变为最大可能。

public static NetTcpBinding CreateStandardNetTcpBinding()
    {
        NetTcpBinding b = new NetTcpBinding(SecurityMode.None);
        b.OpenTimeout = new TimeSpan(0, 5, 0);
        b.ReceiveTimeout = new TimeSpan(0, 5, 0);
        b.SendTimeout = new TimeSpan(0, 5, 0);

        b.MaxReceivedMessageSize = Int32.MaxValue;
        b.MaxBufferSize =(int) b.MaxReceivedMessageSize;
        b.MaxBufferPoolSize = (int) b.MaxReceivedMessageSize;
        b.TransferMode= TransferMode.Buffered;



        b.ReaderQuotas.MaxNameTableCharCount = Int32.MaxValue;
        b.ReaderQuotas.MaxArrayLength = Int32.MaxValue;
        b.ReaderQuotas.MaxBytesPerRead = 4096;
        b.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
        b.ReaderQuotas.MaxDepth = 32;
        return b;
    }

我的第二个问题是为什么这个异常不会在IErrorHandler接口上引发ProvideFault方法。当我从ServiceOperation中引发异常时,ProvideFault方法确实会被引发。 IErrorHandler是否也捕获了WCF框架异常?但是对于这个特殊问题,WCF似乎在捕获序列化异常后突然/立即关闭了通道(因此超时是不可能的)。

  

类型的第一次机会异常   发生'System.Runtime.Serialization.SerializationException'   System.Runtime.Serialization.dll类型的第一次机会异常   发生'System.ServiceModel.CommunicationException'   System.ServiceModel.dll

     

套接字连接已中止。这可能是由错误引起的   处理您的消息或超过接收超时   远程主机或底层网络资源问题。本地套接字   超时为'00:04:59.8939894'。

我的第三个问题是,是否有人知道如何以编程方式添加WCF跟踪侦听器?我在一个插件环境中工作,在该环境中,应用程序没有专用的app.config文件。

谢谢!

--- --- EDIT

是的,这就是问题所在。我打的默认值是64K左右。解决方案是

在servicebehavior属性中设置MaxItemsInObjectGraph值 [ServiceBehavior(IncludeExceptionDetailInFaults = true,MaxItemsInObjectGraph = int.MaxValue)]

并在客户端上设置客户端的大小,如此...

var behaviors = Endpoint.Contract.Operations
                                .Select(o => o.Behaviors.Find<DataContractSerializerOperationBehavior>())
                                .Where(behavior => behavior != null);
        foreach (var serializationBehavior in behaviors)
        {
            serializationBehavior.MaxItemsInObjectGraph = int.MaxValue;
        }

2 个答案:

答案 0 :(得分:1)

不是100%肯定,但我会在这里尝试一些答案。

您可能正在遇到DataContractSerializer的{​​{3}}属性。但是,根据MSDN,默认值为Int32.MaxValue(2,147,483,647),因此如果您没有触及该值,我认为不是这种情况。

另一种选择可能是增加MaxDepth配额,但我的资金将在此之前的MaxItemsInObjectGraph上。

您如何将您创建的绑定分配给服务和客户端? MSDN建议这个代码似乎是服务方面:

OperationDescription operation = host.Description.Endpoints[0].Contract.Operations.Find("MyOperationName");
operation.Behaviors.Find<DataContractSerializerOperationBehavior>().MaxItemsInObjectGraph = 3;

不确定它将如何应用于客户端(不再挖掘更多)。

对于第二个问题,MaxItemsInObjectGraph有点不清楚,但似乎IErrorHandler可能无法处理序列化错误:

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 notified. In this case, you should still make sure that your development environment catches and displays such exceptions to you or makes use of tracing to discover the problem. For more information about tracing, see Using Tracing to Troubleshoot Your Application.

对于您的最后一个问题,似乎可以以编程方式启用WCF跟踪,但有点复杂:MSDN

不确定是否会解决您的问题,但希望它会为您提供一些探索选项。

答案 1 :(得分:0)

对于问题标题中描述的问题,可以使用IDispatchMessageInspector捕获SerializationException。

public class DispatcherExceptionHandler : IDispatchMessageInspector
{

    public object AfterReceiveRequest(
        ref Message request, 
        IClientChannel channel, 
        InstanceContext instanceContext) => null;

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        try
        {
            reply.ToString();
        }
        catch (Exception e)
        {
            var faultCode = FaultCode.CreateSenderFaultCode(null);
            var faultReason = new FaultReason(e.GetBaseException().Message);

            reply = Message.CreateMessage(
                reply.Version,
                MessageFault.CreateFault(faultCode, faultReason),
                null);
        }
    }
}

要设置检查器,请使用Service Behavior

public class HandleDispatcherExceptionsServiceBehavior : IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (var dispatchRuntime in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>().SelectMany(ch => ch.Endpoints).Select(epd => epd.DispatchRuntime))
            dispatchRuntime.MessageInspectors.Add(new DispatcherExceptionHandler());
    }

    // (...)
}