Estava eu desenvolvendo (feliz da vida) uma solução que necessitaria enviar arquivos através do Windows Communication Foundation (WCF), a primeira tentativa funcionou perfeitamente, entretanto todos os testes seguintes geravam uma exceção:

The formatter threw an exception while trying to deserialize the message: Error in deserializing body of request message for operation ‘—’. 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.

Logo, (óbvio) percebi que o tamanho do arquivo esta influenciando este erro, e que o XML que é gerado pelo WCF possuía um limite de caracteres.

A exceção sugere alterar o valor do arquivo de configuração MaxArrayLength. Entretanto mesmo depois de aumentar o valor desse atributo o erro persistia.

Depois de muito tempo procurando em todas as referências possíveis eis que surge a solução:

Precisamos configurar em dois arquivos diferentes algumas coisas.

No web.config do serviço WCF

Entre as tags system.web adicione:

<httpRuntime maxRequestLength="2147483647" />

O atributo maxRequestLength indica o tamanho máximo de upload de arquivos suportados pelo ASP.NET.  O tamanho é especificado em kilobytes  e o padrão é 4096 KB (4 MB).

Depois disso definimos um comportamento de serviço (serviceBehavior).

A tag serviceMetada, informa se haverá publicação de metadados do serviço, o atributo httpGetEnabled informa se será possível recuperar os metadatas usando uma requisição HTTP/GET. O padrão é falso.

A tag serviceDebug, informa os recursos de depuração e informações de ajuda para um serviço WCF, o atributo includeExceptionDetailInFaults informa se deve incluir informações de exceção retornado para o cliente para fins de depuração:

<system.serviceModel>
    <behaviors>
        <serviceBehaviors>
            <behavior name="FileTransferBehavior">
                <serviceMetadata httpGetEnabled="true"/>
                <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>

A tag basicHttpBinding expõem os endPoints que são capazes de comunicar com os serviços baseados em ASMX ou outros serviços que estejam em acordo com o WS-I Basic 1.1.

Aumentei os valores para o máximo possível, para entender cada atributo separadamente, utilize esse link de referência: http://msdn.microsoft.com/en-us/library/ms731325.aspx.

<basicHttpBinding>
    <binding
            name="BigHttpBinding"
            maxBufferSize="2147483647"
            maxBufferPoolSize="2147483647"
            maxReceivedMessageSize="2147483647"
            transferMode="Streamed">
    <readerQuotas
            maxDepth="2147483647"
            maxStringContentLength="2147483647"
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
            maxNameTableCharCount="2147483647" />
</binding>
</basicHttpBinding>

Toda comunicação com um serviço WCF ocorre através de endPoints do serviço. Então definimos endPoint (caso já exista, sobrescreva) e vinculamos com o binding através do atributo bindingConfiguration.

<services>
    <service behaviorConfiguration="FileTransferBehavior" name="namespace.Service">
        <endpoint
            bindingConfiguration="BigHttpBinding"
            name="BasicHttp"
            address=""
            binding="basicHttpBinding"
            contract="namespace.IService" />
    </service>
</services>

Para testar se a configuração esta correta acesse o endereço do serviço (por exemplo, http://localhost:68831/Service.svc), se estiver tudo certo aparecerá as informações de como utilizar o serviço.

No web.config /app.config do projeto que irá consumir o WCF

Entre as tag system.serviceModel adicione:

<behaviors>
    <endpointBehaviors>
        <behavior name="LargeObjectBehavior">
            <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
        </behavior>
    </endpointBehaviors>
</behaviors>

O atributo maxItemsInObjectGraph especifica o número máximo de objetos que o serializador serializa ou desserializa em uma única chamada.

Agora trocamos o httpBinding e endPoint gerado pelo Visual Studio por um semelhante ao que foi colocado no servidor:

<basicHttpBinding>
    <binding
        name="BigHttpBinding"
        maxBufferSize="2147483647"
        maxBufferPoolSize="2147483647"
        maxReceivedMessageSize="2147483647"
        transferMode="Streamed">
        <readerQuotas
            maxDepth="2147483647"
            maxStringContentLength="2147483647"
            maxArrayLength="2147483647"
            maxBytesPerRead="2147483647"
           maxNameTableCharCount="2147483647" />
    </binding>
</basicHttpBinding>

...

<client>
<endpoint
    address="http://localhost:68831/Service.svc"
    binding="basicHttpBinding"
    bindingConfiguration="BigHttpBinding"
    contract="ServiceReference.IService"
    behaviorConfiguration="LargeObjectBehavior" />
</client>

O ServiceReference.IService é contrato de serviço gerado pelo Visual Studio.

Lembrando que ao atualizar o serviço de referência o código do web.config / app.config será sobescrito.

Atenção: o número 2147483647 (não é nenhum número cabalístico, na verdade) é valor máximo de um int no WCF.

é isso, até a próxima.

Referências:

http://rickersilva.net/?p=188

http://msdn.microsoft.com/en-us/library/ms734765.aspx

Sobre Erick

Erick de Oliveira escreveu 19 artigos no blog.

Tagged with →  
Share →

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>