wcfasp.net-3.5wcf-binding

WCF WebServiceHostFactory MaxReceivedMessageSize configuration


I have a RESTful WCF web service called "Palladium" as a project in my VS2008 solution. It is hosted in an ASP.Net 3.5 Web Application using the WebServiceHostFactory implementation via a page called "Palladium.svc".

My service works in a manner similar to the one explained here, whereby POSTs can be received by the service along with other parameters as defined in the URITemplate.

The service works well, and I can receive posted information and work with it.
My problem occurs when the post data exceeds 65k and I get the following error (obtained using system.diagnostics in the web.config and Microsoft Service Trace Viewer).

The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.

Because the service is hosted via the WebServiceHostFactory implementation, the service has default bindings set up for it by the factory. I attempted to override these bindings by providing binding settings and endpoints in the web.config file. When I did this however, I got an error message saying:

System.InvalidOperationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

For request in operation LogStartingDetails to be a stream the operation must have a single parameter whose type is Stream.

LogStartingDetails is the method I'm calling in my RESTful service.

Obviously the LogStartingDetails method doesn't need to have a single parameter whose type is Stream, as when the factory was creating the bindings for me the service was responding well (or more to the point, it didn't need to have a single parameter when the factory was doing the work for me).

After much research and hitting several brick walls, I decided to create my own class that inherits from WebServiceHostFactory and overrides some of the implementation in order to specify the MaxReceivedMessageSize property on the binding.
When I step through the service creation in my factory class via debug, I can see the transport receiving the new MaxReceivedMessageSize and MaxBufferSize values, but they don't appear to do anything and I still end up getting the same The maximum message size quota for incoming messages (65536) has been exceeded. exception thrown.

Below are examples of my service code. If someone could help me figure out what I'm doing wrong here, it would be much appreciated.

Palladium.svc (hosted in the ASP.Net Web Application)

<%@ ServiceHost Language="C#" Debug="true" Service="CDS.PalladiumService.Palladium" Factory="CDS.PalladiumService.MyWebServiceHostFactory" %>

MyWebServiceHostFactory.cs (in the CDS.PalladiumService project)

using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;

namespace CDS.PalladiumService
{
    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 OnOpening()
        {
            base.OnOpening();

            if (base.Description != null)
            {
                foreach (var endpoint in base.Description.Endpoints)
                {
                    var transport = endpoint.Binding.CreateBindingElements().Find<TransportBindingElement>();
                    if (transport != null)
                    {
                        transport.MaxReceivedMessageSize = 5242880;
                        transport.MaxBufferPoolSize = 5242880;
                    }
                }
            }
        }
    }
    
    class MyWebServiceHostFactory : WebServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new MyServiceHost(serviceType, baseAddresses);
        }
    }
}

IPalladium.cs (in the CDS.PalladiumService project)

using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace CDS.PalladiumService
{
    // NOTE: If you change the interface name "IPalladium" here, you must also update the reference to "IPalladium" in Web.config.
    [ServiceContract]
    public interface IPalladium
    {
        [OperationContract]
        [WebInvoke(Method = "POST", UriTemplate = UriTemplate.LogStartingDetails)]
        void LogStartingDetails(string truckId, string palladiumId, Stream postData);
    }
}

Palladium.cs (in the CDS.PalladiumService project)

using System.IO;
using System.ServiceModel.Activation;

namespace CDS.PalladiumService
{
    // NOTE: If you change the class name "Palladium" here, you must also update the reference to "Palladium" in Web.config.
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class Palladium : IPalladium
    {
        public void LogStartingDetails(string truckId, string palladiumId, Stream postData)
        {
            string contents = string.Empty;
            using (var reader = new StreamReader(postData))
            {
                contents = reader.ReadToEnd();
            }

            StreamWriter sw1 =
                File.AppendText(@"C:\log.txt");
            sw1.WriteLine(contents);
            sw1.WriteLine("");
            sw1.Close();

            return;
        }
    }
}

URITemplate.cs (in the CDS.PalladiumService project)

namespace CDS.PalladiumService
{
    public static class UriTemplate
    {
        public const string LogStartingDetails = "/log-starting/{truckId}/{palladiumId}";
    }
}

Solution

  • Ok, for those of you that are looking for a solution to the above problem - I simply made some changes to my web.config file and hosted the service in my asp.net website.

    I changed the <system.serviceModel> section of my web config to the following - this allows for 'Large Message Binding' where the max length of the post is specified in the appropriate section of the config file.

    <system.serviceModel>
    <bindings>
      <webHttpBinding>
        <binding name="largeMessageBinding" maxBufferPoolSize="5242880" maxReceivedMessageSize="5242880" transferMode="Buffered">
          <readerQuotas maxStringContentLength="5242880" maxArrayLength="5242880" maxBytesPerRead="5242880" />
        </binding>
      </webHttpBinding>
    </bindings>
    
    <behaviors>
      <endpointBehaviors>
        <behavior name="largePostEndpointBehavior">
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="CDS.UI.Resources.Services.PalladiumBehavior">
          <serviceMetadata httpGetEnabled="false" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    
    <services>
      <service behaviorConfiguration="CDS.UI.Resources.Services.PalladiumBehavior"
       name="CDS.PalladiumService.Palladium">
        <endpoint address="" binding="webHttpBinding" behaviorConfiguration="largePostEndpointBehavior"
                  bindingConfiguration="largeMessageBinding" contract="CDS.PalladiumService.IPalladium" />
      </service>
    </services>
    
    </system.serviceModel>