2

I am seeing odd behavior when attempting to deserialize a relatively large object graph ~10000 rows with 6 columns on it. I'm sure that the problem is that there is a serialization exception when attempting to deserialize this array from the service back to the client. It works fine for datasets of less then 9000 objects and the console window shows a first chance SerializationException followed by a CommunicationException. I am self hosting this service within a console app since I am using it to integrate with a third party api. My first question is does anyone know have any idea what could be causing this serilization exception?

I am configuring the binding in code the following way on both the client and the server and I think I've turned it to the max possible.

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;
    }

My second question is why is this exception not raising the ProvideFault method on the IErrorHandler interface. When I raise an exception from within the ServiceOperation the ProvideFault method does get raised. Aren't WCF framework exceptions also caught by IErrorHandler? But for this particular problem WCF seems to close the channel abruptly/instantly after catching the serialization exception (so timeout is out of the question).

A first chance exception of type 'System.Runtime.Serialization.SerializationException' occurred in System.Runtime.Serialization.dll A first chance exception of type 'System.ServiceModel.CommunicationException' occurred in System.ServiceModel.dll

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:04:59.8939894'.

My third question is does anyone know how to programmically add a WCF trace listener? I work in a plugin environment where the application doesn't have a dedicated app.config file.

Thanks!

---EDIT---

Yup that was the problem. The default is about 64K which I was hitting. A solution is to

set the MaxItemsInObjectGraph value in the servicebehavior attribute [ServiceBehavior(IncludeExceptionDetailInFaults = true, MaxItemsInObjectGraph = int.MaxValue)]

and set the size on the client on the client like so...

var behaviors = Endpoint.Contract.Operations
                                .Select(o => o.Behaviors.Find<DataContractSerializerOperationBehavior>())
                                .Where(behavior => behavior != null);
        foreach (var serializationBehavior in behaviors)
        {
            serializationBehavior.MaxItemsInObjectGraph = int.MaxValue;
        }
Jason Turan
  • 1,302
  • 12
  • 20
  • Hehe, you got three questions in one! ;) You might be hitting the response size limit. See this questions for more details: http://stackoverflow.com/q/1281269/945456 – Jeff B Dec 31 '13 at 23:21

2 Answers2

1

Not 100% sure, but I'll take a stab at some answers here.

You may be running up against the MaxItemsInObjectGraph property of the DataContractSerializer. However, per MSDN, the default is Int32.MaxValue (2,147,483,647), so if you haven't touched that value I don't think this is the case.

Another option might be to increase the MaxDepth quota, but my money would be on the MaxItemsInObjectGraph before this one.

How are you assigning the binding you create to the service and client? MSDN suggest this code for what appears to be the service side:

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

Not sure how it would apply to the client side (without digging around some more).

For the second question, MSDN is a little unclear, but it appears that serialization errors may not be handled by 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.

For your final question, it appears that enabling WCF tracing programatically is possible, but a bit convoluted: How do you enable wcf tracing without using a config file (programmatically)?

Not sure if any of this will solve your issues, but hopefully it will give you some options to explore.

Tim
  • 28,212
  • 8
  • 63
  • 76
0

As to the problem described in the question's title, it's possible to catch SerializationException using IDispatchMessageInspector.

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);
        }
    }
}

To set the inspector up, use a 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());
    }

    // (...)
}