Service metadata: size does matter

Web services use standards like WSDL and WS-Policy to describe themselves in an interoperable format. When you use WCF to implement your web service, generating the metadata is fortunately not your job (assuming you haven’t defined any custom binding elements).

However, if you have a service that exposes many endpoints or operations (or both), than the WSDL can grow pretty large. Especially when applying security to the service the wsp:Policy parts of the document can became quite large. So large, that under certain circumstances, you won’t be able to consume the resulting XML.

In case of svcutil.exe, this manifests itself with an error message similar to the following:

Metadata contains a reference that cannot be resolved: 'net.tcp://172.22.3.68:55301/mex'.

There is an error in the XML document.

There is an error in the XML document.

The maximum nametable character count quota (16384) has been exceeded while reading XML data. The nametable is a data structure used to store strings encountered during XML processing - long XML documents with non-repeating element names, attribute names and attribute values may trigger this quota. This quota may be increased by changing the MaxNameTableCharCount property on the XmlDictionaryReaderQuotas object used when creating the XML reader.

If the service is defined in the current solution, try building the solution and adding the service reference again.

It could also include the following warning:

 

Warning: The policy expression was not fully imported because it exceeded the maximum allowable complexity. The import stopped at element element_name.

 

As the error message suggests, the large metadata document caused the XmlDictionaryReader to hit a certain quota, in this case, MaxNameTableCharCount.

These quotas aren’t there just for nagging us. XML processing is a memory and CPU intensive task, and these quotas provide a built-in protection against misuse, such as DoS attacks with specially crafted XMLs (to give an idea about such XMLs: thousands of nested nodes, extreme long element names, etc.).

So, the hint is to raise the specific quota we’re hitting in XmlDictionaryReaderQuotas. But svcutil.exe is a compiled binary, so how are we going to accomplish this?

 

We’re lucky again, because svcutil.exe is a WCF application, and the XmlDictionaryReader is actually used by WCF and not svcutil.exe directly. Hence, it’s possible to set the quotas by configuring the binding in svcutil.exe.config. The following example demonstrates this.

 

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <system.serviceModel>

    <client>

      <endpoint name="net.tcp"

                binding="netTcpBinding"

                bindingConfiguration="GenericBinding"

                contract="IMetadataExchange" />

      <endpoint name="http"

                binding="wsHttpBinding"

                bindingConfiguration="SecureBinding"

                contract="IMetadataExchange" />

    </client>

    <bindings>

      <netTcpBinding>

        <binding name="GenericBinding"

                 maxBufferPoolSize="2147483647"

                 maxBufferSize="2147483647"

                 maxReceivedMessageSize="2147483647" >

          <readerQuotas maxDepth="2147483647"

                        maxStringContentLength="2147483647"

                        maxArrayLength="2147483647"

                        maxBytesPerRead="2147483647"

                        maxNameTableCharCount="2147483647" />

          <security mode="None"/>

        </binding>

      </netTcpBinding>

      <wsHttpBinding>

        <binding name="SecureBinding"

    maxBufferPoolSize="2147483647"

                 maxBufferSize="2147483647"

                 maxReceivedMessageSize="2147483647" >

          <readerQuotas maxDepth="2147483647"

                        maxStringContentLength="2147483647"

                        maxArrayLength="2147483647"

                        maxBytesPerRead="2147483647"

                        maxNameTableCharCount="2147483647" />

          <security mode="Message">

            <transport clientCredentialType="Windows" />

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

  </system.serviceModel>

</configuration>

Note that the configuration must be done per binding, and that we actually configure the endpoints that svcutil.exe uses (these have preconfigured names like ‘http’). Correspondingly, other aspects of the bindings (mainly security) must match with those of the service metadata endpoint.

The pre-requisite is that System.servicemodel.dll should be at least version 3.0.4506.722, corresponding to the hotfix https://support.microsoft.com/kb/951111. Versions 3.5 SP1 (3.0 SP2) of the .NET Framework already contain this.