none
How to close the stream using a streamed WCF service? RRS feed

  • Question

  • Suppose we have the following WCF service contract an I implemented this contract on a service using BasicHttpBinding:

    [ServiceContract]
    public interface IFileBackupService
    {
        [OperationContract]
        void UploadFile(FileDataMessage data);
    
        [OperationContract]
        Stream DownloadFile(string filename);
    }

    where FileDataMessage is defined as follows

    [MessageContract]
    public class FileDataMessage : IDisposable
    {
        [MessageHeader(MustUnderstand = true)]
        public string Filename { get; set; }
    
        [MessageBodyMember(Order = 1)]
        public Stream DataStream { get; set; }
    
        public void Dispose()
        {
            if (DataStream != null)
            {
                DataStream.Close();
                DataStream.Dispose();
                DataStream = null;
            }
        }
    }

    Both functions handle a (simple or composite) object of type Stream, so we have to provide to dispose them. Clearly, the client will dispose the stream after uploading a file or after saving to disk the downloaded stream.

    Also the service should dispose the stream: there are two alternative ways to allow the service to close the stream, as explained in this answer.

    1. Setting the OperationBehaviorAttribute.AutoDisposeParameters to true.
    2. Using the OperationCompleted event.

    It should be noted that, in both cases, if the object containing the stream is a composite object, then it must implement the IDisposable interface, as in my FileDataMessage message example.

    My doubts regarding the aforesaid ways to dispose the stream are described below.

    Setting the OperationBehaviorAttribute.AutoDisposeParameters to true. This means that all service methods that use a Stream object (that is, the methods that have a parameter or a return value of type Stream) should enable the AutoDisposeParameters attribute. However, in the case of file upload, the stream should be disposed (by the service) only after the service wrote the file to disk. The following is a sample implementation of my UploadFile method.

    [OperationBehavior(AutoDisposeParameters = true)]
    public void UploadFile(FileDataMessage data)
    {
        string uploadFolder = Path.Combine(m_BaseDirectory, "Upload");
    
        string filepath = Path.Combine(uploadFolder, data.Filename);
        using (FileStream destinationStream = new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            data.DataStream.CopyTo(destinationStream);
        }
    }

    Using this way of disposing the stream, I think that the compiler will write for me the code needed to invoke the Dispose method on the data object at the end of my UploadFile method.

    Using the OperationCompleted event. This article shows an example implementation of this way of disposing the stream and explains that it gives more control, as it is explained also here. Why is this second way of disposing more flexible? Why do some parameters need to be preserved while others need to be disposed?

    Thants a lot for your support!

    Vincenzo



    • Edited by Vincenzo83 Saturday, February 14, 2015 7:25 AM added details
    Saturday, February 14, 2015 7:23 AM

Answers

  • Normally I might suggest wrapping the stream in a custom stream that closes the transaction when disposed, however IIRC WCF makes no guarantees about which threads do what, but TransactionScope is thread-specific. As such, perhaps the better option is to copy the data into a MemoryStream (if it isn't too big) and return that. The Stream.Copy method in 4.0 should make that a breeze, but remember to rewind the memory-stream before the final return (.Position = 0).

    Obviously this will be a big problem if the stream is big, ... but, if the stream is big enough for that to be a concern, then personally I'd be concerned at the fact that it is running in TransactionScope at all, since that has inbuilt time limits, and causes serializable isolation (by default).

    A final suggestion would be to use a SqlTransaction, which is then not thread-dependent; you could write a Stream wrapper that sits around the SqlFileStream, and close the reader, transaction and connection (and the wrapped stream) in the Dispose(). WCF will call that (via Close()) after processing the results.


    Vote if help you

    Tuesday, February 17, 2015 12:58 AM