Buddhike's Weblog

Releasing streams in message contracts

In a lot of WCF streaming applications it's common to see a message contract like this.

[MessageContract]
public class DownloadFileRequest
{
    [MessageHeader]
    private string filename;

    [MessageHeader]
    private int length;

    [MessageBodyMember]
    private Stream fileStream;

    public DownloadFileRequest()
    {
    }      
}

This often brings up the question "How can I release the stream when the streaming is completed?". I just noticed a nice attempt by using a simple polling mechanism in this article http://www.codeproject.com/WCF/WCF_FileTransfer_Progress.asp. But, there is much simpler and elegant way to do this. You simply have to implement IDisposable interface in your message contract and clean up the stream in the Dispose method.

[MessageContract]
public class DownloadFileRequest : IDisposable
{
    [MessageHeader]
    private string filename;
    
    [MessageHeader]
    private int length;
    
    [MessageBodyMember]
    private Stream fileStream;
    
    public DownloadFileRequest()
    {
    }    
    
    public void Dispose()
    {
        if(fileStream != null)
        {
           fileStream.Dispose();
           fileStream = null; 
        }
    }  
}

Once the message has been fully streamed the dispatcher runtime will call Dispose in all input/output parameter objects used to construct the message.

In to a little bit of internals like always ;). I looked up where exactly this happens in the reflector.

As far as I can understand this work is done in MessageRpc.DisposeParameterList method.

private void DisposeParameterList(object[] parameters)
{
    IDisposable disposable = null;
    if (parameters != null)
    {
        foreach (object obj2 in parameters)
        {
            disposable = obj2 as IDisposable;
            if (disposable != null)
            {
                try
                {
                    disposable.Dispose();
                }
                catch (Exception exception)
                {
                    if (DiagnosticUtility.IsFatal(exception))
                    {
                        throw;
                    }
                    this.channelHandler.HandleError(exception);
                }
            }
        }
    }
}

So obviously if our Message contract implements IDisposable it will be perfectly disposed by this function.

Have fun!

Comments

No Comments