Как исправить ошибку MaxItemsInObjectGraph?
У меня есть служба RESTful WCF, и я получаю следующую ошибку: Максимальное количество элементов, которые могут быть сериализованы или десериализованы в графе объектов, - "65536". Измените граф объекта или увеличьте квоту MaxItemsInObjectGraph.
Я думал, что решил это, но, видимо, нет. Вот мой код:
Я использую файл .SVC, который использует пользовательский factory следующим образом:
<%@ ServiceHost Language="C#" Debug="true" Service="myService" Factory="myCustomWebServiceHostFactory" %>
Вот код для пользовательского 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);
}
}
Услуги:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceContract]
[ServiceBehavior(MaxItemsInObjectGraph = int.MaxValue)]
public class myService
{
...service implementation code goes here
}
Клиент:
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
}
ОБНОВЛЕНИЕ: Я немного потрудился. Кажется, что сериализация частично работает на сервере. Я могу сделать GET из браузера, и я получаю обратно все данные в формате XML. Так что это нормально. Кажется, это часть десериализации, которая вызывает ошибку. Если вы посмотрите выше в коде myClient, вы увидите, как я пытаюсь установить свойство MaxItemsInObjectGraph для поведения DataContractSerializer. Правильно ли я делаю это?
Ответы
Ответ 1
Я понял это!!! Мой код клиента был неправильным. Я установил MaxItemsInObjectGraph после того, как я уже создал свой канал. Таким образом, свойство MaxItemInObjectGraph не повлияло на уже созданный канал. (Этот материал WCF так запутан для меня - я обычно просто копирую и вставляю код, не зная, что я делаю) Вот скорректированный код:
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();
Ответ 2
Вам нужно установить MaxItemsInObjectGraph на dataContractSerializer, используя поведение как для клиента, так и для службы. См. here для примера.
Ответ 3
На стороне клиента другой способ достичь этого - создать пользовательскую реализацию IEndPointBehavior - см. http://canbilgin.wordpress.com/2010/06/25/how-to-set-maxitemsinobjectgraph-programmatically-for-client/.
Это особенно полезно при реализации клиента с помощью 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;
}
}
Ответ 4
Вы возвращаете общий список или массив, размер которого превышает 65536. В ваших запросах, используя выбранный верх 60000 или не добавляя более 60 тыс. элементов, вы решите свою проблему.