El problema es sencillo de explicar, su solución me tomó horas y horas de leer blogs, y hablar con dos amigos. Puede ser simple pero lo complejo es poder poner todas las partes en su lugar.
El Error
He creado una solución de ejemplo. La solución son dos proyectos: un WCF Service normal y una aplicación Windows Forms. El propósito: subir un archivo desde el cliente hasta el servicio, y ya; El error,
The formatter threw an exception while trying to deserialize the message: Error in deserializing body of request message for operation ‘CrawlResponse’. The maximum array length quota (16384) has been exceeded while reading XML data. This quota may be increased by changing the MaxArrayLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader.
Y aqui empieza uno a padecer un poco. Existen miles de soluciones en internet, blogs, artículos, comentarios, pies de página, etc. Todos indican una que otra cosa. En medio de mi desespero por no poder entender o implementar dichas soluciones veía como todo el mundo agradecía enormemente la ayuda de esos posts y yo seguía elevando improperios al aire en contra del fatídico número 16384.
Así que escribo este post para evitarme dolores de cabeza futuros, y de paso para tratar de explicar qué hay que hacer para solucionar el error en menos tiempo que volverlo a decir. Finalmente, uno “simplemente” debe hacer lo siguiente:
La Solución
Cuando la excepción indica que debemos modificar el valor de MaxArrayLength del archivo de configuración, en verdad nos manda a dos sitios diferentes. Al config del proyecto que tiene el servicio WCF y al config del proyecto cliente. Lo interesnte es que en VS2010 cuando creamos un servicio WCF en un proyecto, no se genera ningún código de configuración en el proyecto servidor. En versiones anteriores sí se generaba y era más fácil sincronizar ambos puntos de la conexión.
En el servidor
Completamos la configuración del servicio en el config creando un nuevo behavior para nuestro servicio
<system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="FileTransferBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors>
Luego creamos el binding así:
<basicHttpBinding> <binding name="BigHttpBinding" maxBufferSize="20971520" maxBufferPoolSize="20971520" maxReceivedMessageSize="20971520" transferMode="Buffered"> <readerQuotas maxDepth="64" maxStringContentLength="20971520" maxArrayLength="20971520" maxBytesPerRead="4096" maxNameTableCharCount="32768" /> </binding> </basicHttpBinding>
Los valores que deben usar en los atributos deben estar de acuerdo a las necesidades de su servicio y del negocio. En este caso, el ejemplo era subir un archivo, digamos de hasta 20Mb. Inicialmente conuno de 32K ya aparecía la excepción.
Finalmente configuramos el endpoint del servicio en el servidor.
<services> <service behaviorConfiguration="FileTransferBehavior" name="Filetransfer.Service1"> <endpoint bindingConfiguration="BigHttpBinding" name="BasicHttp" address="" binding="basicHttpBinding" contract="Filetransfer.IService1"> </endpoint> </service> </services>
Donde Filetransfer.IService1 y Filetransfer.Service1 son el nombre de la interfaz del servicio y la clase que implementa el servicio respectivamente, con su namespace.
Resumiendo, hemos creado un behavior que no quita ni pone mucho en términos de configuración del servicio en el servidor. Creamos un binding personalizado del tipo basicHttpBinding que tiene una configuración que permite el envío de mensajes de hasta 20MB a través del servicio y finalmente creamos un endpoint para el servicio cuya configuración de behavior es la que definimos inicialmente y cuya configuración de binding es, en este caso, BigHttpBinding.
Finalmente agregamos la siguiente línea en el config
<system.web> <httpRuntime maxRequestLength="20971520"/> </system.web>
Que sobreescribirá el tamaño máximo de un request al servidor. Debemos curarnos en salud en todo aspecto.
Ahora pasemos al cliente.
Al momento de crear la referencia al servicio, Visual Studio genera un código básico de configuración en el cliente. Para poder crear la referencia es necesario que el servicio esté funcionando. Se prueba fácil si al ver Servie1.svc en un navegador obtenemos el siguiente resultado:
Luego podemos generar la referencia al servicio en nuestra aplicación cliente y modificar la configuración generada, así:
Creamos un endpointBehavior
<system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="LargeObjectBehavior"> <dataContractSerializer maxItemsInObjectGraph="2147483647"/> </behavior>
Luego un binding tipo httpBinding
<basicHttpBinding> <binding name="BigHttpBinding" maxBufferSize="20971520" maxBufferPoolSize="20971520" maxReceivedMessageSize="20971520" transferMode="Buffered"> <readerQuotas maxDepth="64" maxStringContentLength="20971520" maxArrayLength="20971520" maxBytesPerRead="4096" maxNameTableCharCount="32768" /> </binding> </basicHttpBinding>
Que es idéntico al que está en el servidor.
Finalmente configuramos el endpoint generado para que use nuestros nuevos binding y behavior.
<client> <endpoint address="http://localhost:8841/Service1.svc" binding="basicHttpBinding" bindingConfiguration="BigHttpBinding" contract="ServiceReference1.IService1" name="" behaviorConfiguration="LargeObjectBehavior" /> </client>
Donde ServiceReference1.IService1 es el contrato del servicio generado por el asistente de VS2010.
No hay que olvidar que al actualizar la referencia del servicio es posible que se genere nuevo código y siempre debemos revisar que ambos extremos de la comunicación estén coherentes o vamos a tener problemas. Con esta configuración garantizamos eso, y que los valores de los atributos de los bindings serán propagados a través de nuestra aplicación y sus componentes.
Debo agradecer a @warnov y @jkpelaez por su valiosísimo aporte en la solución de este problema que espero haber explicado con buen detalle.

