11
votes

I have a RESTful WCF service and I am getting the following error: Maximum number of items that can be serialized or deserialized in an object graph is '65536'. Change the object graph or increase the MaxItemsInObjectGraph quota.

I thought I had resolved this but apparently not. Here is my code:

I use a .SVC file that uses a custom factory like this:

<%@ ServiceHost Language="C#" Debug="true" Service="myService" Factory="myCustomWebServiceHostFactory" %>

Here is the code for the custom factory

public class myCustomWebServiceHost : WebServiceHost
{
    public myCustomWebServiceHost()
    {
    }

    public myCustomWebServiceHost(object singletonInstance, params Uri[] baseAddresses)
        : base(singletonInstance, baseAddresses)
    {
    }

    public myCustomWebServiceHost(Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
    }

    protected override void OnOpening()
    {
        foreach (var endpoint in Description.Endpoints)
        {
            var binding = endpoint.Binding as WebHttpBinding;
            if (binding != null)
            {
                const int fiveMegaBytes = 5242880;
                binding.MaxReceivedMessageSize = fiveMegaBytes;
                binding.MaxBufferSize = fiveMegaBytes;
                binding.MaxBufferPoolSize = fiveMegaBytes;
            }
        }
        base.OnOpening();
    }
}

class myCustomWebServiceHostFactory : WebServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new myCustomWebServiceHost(serviceType, baseAddresses);
    }
}

Service:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceContract]
[ServiceBehavior(MaxItemsInObjectGraph = int.MaxValue)]
public class myService
{
...service implementation code goes here
}

Client:

public class myClient
{
    WebChannelFactory<IMyService> cf;
    IMyService channel;

    public myClient()
    {
        WebHttpBinding _binding = new WebHttpBinding();
        _binding.MaxBufferPoolSize = 5000000;
        _binding.MaxBufferSize = 5000000;
        _binding.MaxReceivedMessageSize = 5000000;
        _binding.TransferMode = TransferMode.Streamed;
        _binding.ReceiveTimeout = new TimeSpan(0, 0, 30);
        _binding.ReaderQuotas.MaxArrayLength = 5000000;
        Uri _uri = new Uri("http://myserviceurl");
        cf = new WebChannelFactory<IMyService>(_binding, _uri);
        channel = cf.CreateChannel();
        foreach (OperationDescription op in cf.Endpoint.Contract.Operations)
        {
            var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
            if (dataContractBehavior != null)
            {
                dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
            }
        }
    }
        ...client implementation code goes here
}

UPDATE: I have done a little troubleshooting. It seems that the serialization part is working fine on the server. I am able to do a GET from a browser and I receive back all of the data in XML format. So that is working fine. It seems to be the deserialization part that is causing the error. If you look above in the code for myClient you will see how I am attempting to set the MaxItemsInObjectGraph property for the DataContractSerializer behavior. Am I doing this correctly?

4
I read somewhere that setting MaxItemsInObjectGraph in attribute doesn't work. Try to set it in configuration - it is service behavior called dataContractSerializer. - Ladislav Mrnka
Ok. That helps. But this is where I get confused. From what I can tell because I am using an SVC file and a custom Factory, my implementation will ignore any settings in my config file. So I don't know how to set the behavior in code. - Corey Burnett
FYI - this is where I found the solution that I implemented: stackoverflow.com/questions/4812668/… - Corey Burnett
Setting behavior from code in the factory or custom service host will have probably the same effect like using the attribute because you will search the behavior in service's description and change its configuration. I'm really not sure if I'm right with my former statement but I remember that I read about some problems with this. - Ladislav Mrnka

4 Answers

23
votes

I figured it out!!! My client code was wrong. I was setting the MaxItemsInObjectGraph after I had already created my channel. So the MaxItemInObjectGraph property had no effect on the already created channel. (This WCF stuff is so confusing to me - I am usually just copying and pasting code without really knowing what I am doing) Here is the corrected code:

        WebHttpBinding _binding = new WebHttpBinding();
        _binding.MaxBufferPoolSize = 5000000;
        _binding.MaxBufferSize = 5000000;
        _binding.MaxReceivedMessageSize = 5000000;
        _binding.TransferMode = TransferMode.Streamed;
        _binding.ReceiveTimeout = new TimeSpan(0, 0, 30);
        _binding.ReaderQuotas.MaxArrayLength = 5000000;
        Uri _uri = new Uri(http://myserviceurl);
        cf = new WebChannelFactory<IMyService>(_binding, _uri);
        foreach (OperationDescription op in cf.Endpoint.Contract.Operations)
        {
            var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>();
            if (dataContractBehavior != null)
            {
                dataContractBehavior.MaxItemsInObjectGraph = int.MaxValue;
            }
        }
        channel = cf.CreateChannel();
4
votes

You need to set the MaxItemsInObjectGraph on the dataContractSerializer using a behavior on both the client and service. See here for an example.

0
votes

On the client side, another way to achieve this is to create a custom IEndPointBehavior implementation - refer to http://canbilgin.wordpress.com/2010/06/25/how-to-set-maxitemsinobjectgraph-programmatically-for-client/.

This is particularly useful when implementing a client with the Windsor WcfFacility

public class ReaderQuotaExtension : IEndpointBehavior
{
    #region Implementation of IEndpointBehavior

    public void Validate(ServiceEndpoint endpoint)
    {
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        ModifyDataContractSerializerBehavior(endpoint);
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        ModifyDataContractSerializerBehavior(endpoint);
    }

    #endregion

    private static void ModifyDataContractSerializerBehavior(ServiceEndpoint endpoint)
    {
        foreach (var behavior in endpoint.Contract.Operations.Select(operation => operation.Behaviors.Find<DataContractSerializerOperationBehavior>()))
        {
            behavior.MaxItemsInObjectGraph = 2147483647;
        }
    }
0
votes

You are returning a generic list or an array which has a size of more than 65536. in your queries, using a select top 60000 or not adding more than 60k elements will solve your problem.