Monday, June 1, 2009

Stream operation in WCF

WCF provides the support for Stream object. It typically recommends the developer to handle the message which size is too large as Stream object for the sake of high performance.

However, there are some constraints on Stream operation to note:
1. The constraint of Binding

The valid bindings include BasicHttpBinding, NetTcpBinding and NetNamePipeBinding for Stream operation. In addition, we can’t use Reliable Messaging while handling the Stream object. If you are considering about the security of message, this way is not a good choice.

2. The constraint of Stream object

The object, you want to transport as a parameter with WCF Operation, must be serializable. Unfortunately, FileStream class can’t be serialized. We have to use Stream, MemoryStream. The Stream class is the main option for handling a stream object.

It is very interesting of transform between FileStream and Stream class. For example, the following implementation of the operation in a service:
public Stream TransferDocument(Document document)
     FileStream stream
= new FileStream
                             (document.LocalPath, FileMode.Open, FileAccess.Read);
return stream;

Note, the type of return value of TransferDocument() method is Stream. But the ture type should be FileStream. Due to FileStream is the subclass of Stream, so it is no problem according to polymorphism of OO. When the client want to invoke TransferDocument() method, we can’t assign the return value to FileStream object in fact:
FileStream stream = m_service.TransferDocument(doc);

The value of stream object is null now. So we must do like this:
Stream stream = m_service.TransferDocument(doc);

It is strange that WCF can’t serialize the Length property of Stream object. On the client side, we can not use the Length property. If you want to do, it will throw a NotSupportedException.

3. The constraint of TransferMode

The default value of TransferMode is setted to Buffered. If you want to use Stream operation, you must change the default setting of TransferMode. We can set it to Streamed, StreamedRequest or StreamedResponse according to the different cases.

4. The constraint of MaxReceiveMessage

The default value of MaxReceiveMessage property is 64kb. If the size of transported stream object exceeds the setting value of MaxReceiveMessage, It will throw a CommunicationException during the client invokes the operation of service to handle this stream object. So we should change the value depending on the specific situation. The value ranges from 1 to 9223372036854775807(i.e. Int32.MaxValue). If the setting value is outside the range, the program can not be compiled successfully. Set its value in programmatic:
binding.MaxReceivedMessageSize = 120000;

And set it in administrative:
<binding …… maxReceivedMessageSize=120000/>

5. The constraint of Operation Parameters

WCF applies the strict constraint on the parameters of operation including stream objects. There can be only one stream object as the parameter(in, out, ref parameter or return value) in the method signature. So these definitions of the method are all invalid as below:
void Transfer(Stream s1, Stream s2);
void Transfer(Stream s1, out Stream s2);
void Transfer(Stream s1, ref Stream s2);
Stream Transfer(Stream stream);

If you define the method like above, it will occur the run-time error.

6. The constraint of Instance Activation

Because we can only use the BasicHttpBinding, NetTcpBinding or NetNamedPipeBinding in the stream operaiton, it will impact on the mode of instance activation, in particular Session mode. First, BasicHttpBinding doesn’t support Session mode. Secondly, although the other bindings(NetTcpBinding or NetNamedPipeBinding) support Session mode, we can’t set the value of ReliableSession to the true because the stream operation doesn’t support reliable messaging. So if you set the value of SessionMode to SessionMode.Required for the service, it will throw an exception.

In fact, the stream operation(i.e. the value of TransferMode is not Buffered) itself doesn’t support Session mode. Even we set the value of SessionMode to Allowed, and set the value of InstanceContextMode to PerSession while using NetTcpBinding, the behavior of service is still PerCall mode. And the value of SessionId(get it through by OperationContext.Current.SessionId) should be null at this time.

Finally, I recommend you increase the value of SendTimeOut property because the calling a large stream object will last too long time. For example, set its value to 10 minutes in programmatic:
binding.SendTimeout = TimeSpan.FromMinutes(10);

Or set it in administrative:
<binding …… sendTimeout=00:10:00/>

Note, the configuration of Binding on the service and client side must keep in consistent.

This article on C# Corner.