wcfrestwcf-bindingmaxreceivedmessagesizewebservicehost

How to override WebServiceHostFactory MaxReceivedMessageSize?


There are a lot of similar questions out there, but I have tried every solution in every one of them to no avail.

We have a web service that initialises with WebServiceHostFactory, but if any more than 64k is thrown at it, we get a '400 Bad Request'. Normally, this would just be resolved by bumping up the MaxReceivedMessageSize, MaxBufferSize and MaxBufferPoolSize. The problem is that using the WebServiceHostFactory, the Web.Config is completely ignored. No changes I make in the ServiceModel section reflect in the service at all.

It would be nice to just completely ditch WebServiceHostFactory and set up the web.config from scratch, but our service will not run without it. One of the methods has a stream parameter as well as some other string params. Without the factory, we get

System.InvalidOperationException: For request in operation Test to be a stream the operation must have a single parameter whose type is Stream

So it is not an option to remove the factory. I can't work out exactly what the factory is doing that fixes this error but I spent 4 days on it and never got anywhere.

I've also tried overriding MaxReceivedMessageSize programatically, with some examples I found on around the place:

protected override void OnOpening()
        {
            base.OnOpening();
            foreach (var endpoint in Description.Endpoints)
            {

                //var binding = endpoint.Binding as WebHttpBinding;
                //if (binding != null)
                //{
                //    binding.MaxReceivedMessageSize = 20000000;
                //    binding.MaxBufferSize = 20000000;
                //    binding.MaxBufferPoolSize = 20000000;
                //    binding.ReaderQuotas.MaxArrayLength = 200000000;
                //    binding.ReaderQuotas.MaxStringContentLength = 200000000;
                //    binding.ReaderQuotas.MaxDepth = 32;
                //}


                //var transport = endpoint.Binding.CreateBindingElements().Find<HttpTransportBindingElement>();
                //if (transport != null)
                //{
                //    transport.MaxReceivedMessageSize = 20000000;
                //    transport.MaxBufferPoolSize = 20000000;
                //}


                var newTransport = new HttpTransportBindingElement();
                newTransport.MaxReceivedMessageSize = 20000000;
                newTransport.MaxBufferPoolSize = 20000000;
                endpoint.Binding.CreateBindingElements().Add(newTransport);
            }
        }

The first doesn't work as the factory creates a CustomBinding which cannot be cast to a WebHttpBinding. The second doesn't work as it seems the binding elements are read only - no matter what I set the elements to, nothing changes, which I have verified by reading the values back after 'changing' them. The third was a last ditch attempt to try to throw a new binding element in there, but of course this failed as well.

Now we are completely at a loss. How can we get this thing to run? You think it would be so simple!

Thanks guys

Web.Config

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <compilation targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="rest" maxReceivedMessageSize="500000000" />
      </webHttpBinding>
    </bindings>
    <services>
      <service name="SCAPIService" behaviorConfiguration="ServiceBehaviour">
        <endpoint address="" binding="webHttpBinding" bindingConfiguration="rest" contract="ISCAPIService" behaviorConfiguration="web">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviour">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
  </system.webServer>
</configuration>

Edit, using a suggested fix that has been unsuccessful so far:

public class MyServiceHost : WebServiceHost
    {
        public MyServiceHost()
        {
        }

        public MyServiceHost(object singletonInstance, params Uri[] baseAddresses)
            : base(singletonInstance, baseAddresses)
        {
        }

        public MyServiceHost(Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
        }

        protected override void ApplyConfiguration()
        {
            base.ApplyConfiguration();
            APIUsers.TestString += "here" + Description.Endpoints.Count.ToString();
            foreach (var endpoint in this.Description.Endpoints)
            {
                var binding = endpoint.Binding;
                APIUsers.TestString += binding.GetType().ToString();
                if (binding is WebHttpBinding)
                {
                    var web = binding as WebHttpBinding;
                    web.MaxBufferSize = 2000000;
                    web.MaxBufferPoolSize = 2000000;
                    web.MaxReceivedMessageSize = 2000000;
                }
                var myReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas();
                myReaderQuotas.MaxStringContentLength = 2000000;
                myReaderQuotas.MaxArrayLength = 2000000;
                myReaderQuotas.MaxDepth = 32;
                binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, myReaderQuotas, null);
            }
        }
    }

    class MyWebServiceHostFactory : WebServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new MyServiceHost(serviceType, baseAddresses);
        }
    }

Solution

  • If you are implementing your own custom service host you should be able to override a method called "ApplyConfiguration" where you can associate all the configuration properties you need for your binding. Some sample code as shown below:

    EDIT: Adding my servicehost factory implementation

    public class MyServiceHost : System.ServiceModel.ServiceHost
    {
        public MyServiceHost () { }
    
        public MyServiceHost (Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)
        {
    
        }
    
        public MyServiceHost (object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses)
        {
    
        }
    
    protected override void ApplyConfiguration()
                {
                    Console.WriteLine("ApplyConfiguration (thread {0})", System.Threading.Thread.CurrentThread.ManagedThreadId);
                    base.ApplyConfiguration();
                    foreach (ServiceEndpoint endpoint in this.Description.Endpoints)
                    {
                        Binding binding = endpoint.Binding;
                        var binding = endpoint.Binding;
                        if(binding is WebHttpBinding)
                        {
                            var web = binding as WebHttpBinding;
                            web.MaxBufferSize = 2000000;
                            web.MaxReceivedMessageSize = 2000000;
                        }
                        var myReaderQuotas = new XmlDictionaryReaderQuotas();
                        myReaderQuotas.MaxStringContentLength = 5242880;
                        binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, myReaderQuotas, null); 
                    }
                }
    
    }
    

    The above does override your configuration of each binding and sets the MaxStringContentLength.

    public sealed class MyServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
        {
            public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
            {
                return base.CreateServiceHost(constructorString, baseAddresses);
            }
    
            protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
            {
                return new MyServiceHost(serviceType, baseAddresses);
            }
        }
    

    Now my Service.svc file markup has this:

    <%@ ServiceHost Language="C#" Debug="true" Factory="Sample.MyServiceHostFactory" Service="Sample.ReaderQuotasService" CodeBehind="ReaderQuotasService.svc.cs" %>