Friday, December 9, 2011

Streaming in WCF


There are scenarios when you must be able to send or receive large amount of data or files using a service in your application. WCF supports streaming and I will show you how to achieve that.

There are 4 types of TransferMode in WCF

using System;namespace System.ServiceModel
{
    public enum TransferMode    

    {
        Buffered = 0,
        Streamed = 1,
        StreamedRequest = 2,
        StreamedResponse = 3,
    }
}


The default one is Buffered. This means that the sender/receiver cannot process the message until the whole data is in one place. We must set our binding to Streamed to enable streaming.

Next thing is to add an operation contract that must satisfy 2 conditions:
1. The parameter that holds the data to be streamed must be the only parameter in the method.
2. At least one of the types of the parameter and return value must be either Stream, Message, or IXmlSerializable.  

In short, this means that the method must have only one parameter which is a Stream, otherwise the return type must be Stream,Message, etc...

Step 1. Create a WCF service application
In VisualStudio, File->New Project->Wcf->Wcf Service Application

Step 2. Add an operation contract with a Stream parameter
Here is my service contract:
[ServiceContract]
public interface IService1
{
    [OperationContract]
    bool SendStream(Stream stream);
}

Step 3. Create a binding in the configuration file, which supports streaming

We have to set the transferMode to Streamed and increase the message size attributes. I have used a regular basicHttpBinding.

<bindings>
      <basicHttpBinding>
        <binding name="streaming" maxReceivedMessageSize="67108864" transferMode="Streamed">         
        </binding>
      </basicHttpBinding>
    </bindings>


After the binding is created, add the endpoint and service tags like in the image above.

Step 4. Implement the operation contract in Service.svc

public bool SendStream(Stream stream) 
{
    try
    {
         using (FileStream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "file", FileMode.Create))
         {
             stream.CopyTo(fs);
          }
          return true;
      }
      catch (Exception ex)
      {
          return false;
       } 
}


Step 5. Critical step. 

 I lost many hours trying to understand why this almost complete configuration doesn't work with files larger than 3-4 MBytes.

The reason is that even if we enable streaming and we increase the message size, ASP.NET doesn't know about WCF limitations. So the solution is to add httpRuntime in system.web element:
<httpRuntime maxRequestLength="67108864"/>


Step 6. Create the client and consume the service
Create a simple console application and add a service reference of the created service. You must have a file called 'largefile' without any extension in the bin\debug\ folder of your console application. Mine has ~20 MBytes.


Now consume the service:

static void Main(string[] args)
        {
            try
            {
                using (ServiceReference1.Service1Client proxy = new ServiceReference1.Service1Client())
                {
                    using (FileStream fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + "largefile", FileMode.Open))
                    {
                        var result = proxy.SendStream(fs);
                    }
                }
            }
            catch (Exception ex)
            {
            }
        }

The result should be true, which means that the streaming was successful.
Happy coding! :)
Download code

4 comments:

  1. I think you forgot step where to add end point of the service (thanks you have provide the source code! :) ).

    Step X: Service end point
    Put this xml syntax in web.config of your WCF service, right bellow or after xml syntax on the Step 3.





    ReplyDelete
  2. But client side configuration seems to be wrong; its in buffered mode.

    ReplyDelete