Cover

Using Large Message Requests in Silverlight with WCF

September 10, 2009
No Comments.

Today I was working with a client and ran into a problem I didn’t expect. This particular problem had to do with Silverlight consuming a WCF Service. Sometimes WCF causes me to spew four letter words. There is a class of WCF problems that cause this: connection rejection. WCF has been designed to prevent common DDoS and other attacks that could cause servers to fail or at least not serve honest requests. To this end the default size of a request is quite small. In fact, its usually 64K in size. This size is fine for most every request but occassionally when a client wants to send a collection of things to the server this size is too small. But we’ll get to that in a minute. First, some background…

This particular client has a large service that’s been working for quite a while, then suddenly a single call he has was being rejected. The new service call he was making was sending back a list of a couple of hundred of POCO classes. He had been downloading large lists before so on the surface this should have worked. But it didn’t.

The cause was that the default WCF limits were rejecting the connection. This type of error where its just failing, even Fiddler isn’t helpful. There is no return code, the connection is just severed. Debugging it requires some trial and error which sucks.  The calls that were returning a lot of data were working fine because the default ClientConfig file looks like this:

<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="CustomBinding_MyService">
          <binaryMessageEncoding />
          <httpTransport maxReceivedMessageSize="2147483647"
                       maxBufferSize="2147483647">
            <extendedProtectionPolicy policyEnforcement="Never" />
          </httpTransport>
        </binding>
      </customBinding>
    </bindings>
    <client>
      <endpoint address="http://127.0.0.1.:8000/MyService.svc"
                binding="customBinding"
                bindingConfiguration="CustomBinding_MyService"
                contract="MyServices.MyService"
                name="CustomBinding_MyService" />
    </client>
  </system.serviceModel>
</configuration>

The maxReceivedMessageSize and maxBufferSize both accept these large responses from the server. But the reverse isn’t true. The server’s defaults are about 64K. Since we got it working, I wanted to show what we did so maybe others could use these server settings to address it.

The trick is to change the customBinding in the web.config to use larger defaults. I picked 2MB as it its a reasonable size. Of course setting them to 2GB like shown above will work but it does leave you more vulnerable to attacks.  Pick a size that isn’t larger than your largest request but isn’t overly large. Its a guessing game.  To set these, you need to add them to your web.config is to put them on the httpTransport node:

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior name="TestLargeWCF.Web.MyServiceBehavior">
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug includeExceptionDetailInFaults="false"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <bindings>
    <customBinding>
      <binding name="customBinding0">
        <binaryMessageEncoding />
        <httpTransport maxReceivedMessageSize="2097152"
                     maxBufferSize="2097152" 
                     maxBufferPoolSize="2097152" />
      </binding>
    </customBinding>
  </bindings>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
  <services>
    <service behaviorConfiguration="Web.MyServiceBehavior"
             name="TestLargeWCF.Web.MyService">
      <endpoint address=""
                binding="customBinding"
                bindingConfiguration="customBinding0"
                contract="TestLargeWCF.Web.MyService"/>
      <endpoint address="mex"
                binding="mexHttpBinding"
                contract="IMetadataExchange"/>
    </service>
  </services>
</system.serviceModel>

That’s the trick. Hope it helps you not waste your day like I wasted mine ;)