3

I have a requirement for a WCF service operation that accepts a large Stream, processes it and returns that stream.

I used an MSDN article on Large Data Streaming as a reference to what I need. I followed the advice in that article.

Asking the question in advance:

  1. I would like to know why the generated service operation does not have a return type when I specified it in the contract?

  2. If that is expected behaviour, how should I get it to pass on a stream and return a processed stream?

Details:

Because I need to accompany both the input and return streams with MetaData, I decorated the classes with MessageContract attributes, as is required.

Here is a brief run-down of my implementation:

Message Contracts:

[MessageContract]
public class InputStreamMessage
{
    [MessageHeader]
    public InputStreamHeader Header { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Data { get; set; }

}

[MessageContract]
public class OutputStreamMessage
{
    [MessageHeader]
    public OutputStreamHeader Header { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Data { get; set; }

}

Service Contract:

[ServiceContract]
public interface IStreamService
{
    [OperationContract]
    OutputStreamMessage ProcessStream(InputStreamMessage input);
}

Service Implementation:

 public OutputStreamMessage DoStreamOperation(InputStreamMessage input)
 {
    //Some logic that assigns re
    OutputStreamMessage output = DoSomeNonBufferedProcessing(input);

    return output;
 }

Client-side:

On the client-side, I then generate the service reference, and call the service as below:

private void PerformStreamOperation()
{
    try
    {
        //
        StreamServiceReference.StreamServiceClient client = new StreamServiceReference.StreamServiceReferenceClient();
        client.Open();

        //Set Header and Parameters
        InputMessageHeader header = new InputMessageHeader();

        //...                
        //... initialize header data here
        //...                

        //... do some operation to get input stream
        var inputstream = SomeOperationToGetInputStream();

        //Perform Service stream action
        //         ____ [ Why does the generated method have the following signature, retuning void?]
        //        |     [ If this is expected, how do I use it? ]
        //        |
        //        V 
        client.DoStreamOperation(header, ref inputstream); 


        //...                
        //... Do what you wish with data
        //...                

    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message.ToString(), "Stream Processing Error");
    }
}

The MSDN article uses the exact same contract that exists in the official WCF samples.

Stream EchoStream(Stream data)

But no example of an equivalent MessageContract implementation. The sample version does an expected return.

Update

  • I noticed that the service reference has Task/Asynchronous methods that are generated with the expected method signature. Maybe that means when using MessageContract with a Stream property, returning a similarly structured object, then you will have to call it Asynchronously. I have not seen it documented anywhere. Will try using the methods - Did not work as we want synchronous operations.
  • I have also tried using the ChannelFactory as an alternative to the generated proxy client:

      EndpointAddress endpoint = new EndpointAddress("net.tcp://localhost:9910/StreamService");
    
      channelFactory = new ChannelFactory<IStreamService>("netTcpStreamedEndPoint");
      channelFactory.Endpoint.Contract.SessionMode = SessionMode.Allowed;
      IStreamService service = channelFactory.CreateChannel();
    
tinonetic
  • 7,751
  • 11
  • 54
  • 79

1 Answers1

2

Im sorry for replying in an answer (I dont have reputation for comments).

I am working on simmilar project as you are - I have service, that accepts large stream of data (using MessageContracts), process it and then client can download these data.

First of all - the input parameters at:

 client.DoStreamOperation(header, ref inputstream); 

shows, that it seems like you did not generated service proxy with MessageContracts included (see http://blogs.msdn.com/b/zainnab/archive/2008/05/13/windows-communication-foundation-wcf-what-the-hell-is-always-generate-message-contracts.aspx). That should provide you OutputStreamMessage and InputStreamMessage contracts on client side.

With messageContracts properly generated, I can write both of these in my code, without receiving compile error:

client.DoStreamOperation(inputStreamMessage)

and

   StreamServiceReference.StreamServiceClient.OutputStreamMessage outputMessage = client.DoStreamOperation(inputStreamMessage)

But basicaly the first one has no use. And of course I have to create InputStreamMessage object first:

StreamServiceReference.StreamServiceClient.InputStreamMessage inputStreamMessage = new StreamServiceReference.StreamServiceClient.InputStreamMessage();

If you would like, I can post some samples of my MessageContracts.

Also, please take a look at this article: http://www.codeproject.com/Articles/166763/WCF-Streaming-Upload-Download-Files-Over-HTTP . My messagecontracs looked simmilar at the early stage of the project


EDIT: The session mode is set like this:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]

The reason for that is that I need to maintain information(state) about objects, that are common for multiple clients. But this should not affect streaming.

Here is my binding. Im using basic http:

      <basicHttpBinding>
        <binding name="TransferBinding" transferMode="Streamed" maxReceivedMessageSize="10067108864">
        </binding>
      </basicHttpBinding>

For uploading, Im using this kind of messagecontract:

    [MessageContract]
        public class RemoteFileInfo : IDisposable
        {
            [MessageHeader(MustUnderstand = true)]
            public string FileName;

            [MessageBodyMember]
            public System.IO.Stream FileByteStream;
}

This is the body of method defined at client-side, that calls StartUpload() defines at service side (You need to define filePath that leads to the file you want to upload):

using (System.IO.FileStream stream = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
                           // start service client
                CalculationServiceClient client = new CalculationServiceClient();     

                RemoteFileInfo remoteFileInfo = new RemoteFileInfo(); ;
                remoteFileInfo.FileName = TextBox1.Text;
                remoteFileInfo.FileByteStream = stream;

                // upload file
                client.StartUpload(remoteFileInfo);

                // close service client
                client.Close();
                uploadStream.Close();
            }
        }

Then, I define StartUpload() operationContract at service side. The inside of StartUpload contract looks for example like this:

public void StartUpload(RemoteFileInfo fileInfo)
        {

            string filePath = define your filePath, where you want to save the file;           

            int chunkSize = 2048;
            byte[] buffer = new byte[chunkSize];

            using (System.IO.FileStream writeStream = new System.IO.FileStream(filePath, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write))
            {
                do
                {
                    // read bytes from input stream (provided by client)
                    int bytesRead =  fileInfo.FileByteStream.Read(buffer, 0, chunkSize);
                    if (bytesRead == 0) break;

                    // write bytes to output stream
                    writeStream.Write(buffer, 0, bytesRead);
                } while (true);

                writeStream.Close();
            }
        }
JakubJ
  • 235
  • 4
  • 13
  • Hi @JakubJ. Thanks for replying. Would you mind posting an example implementation? What `SessionMode` did you also use and how is your binding config? Is it a continuous stream of data or do you close it at some point at the service operation? The codeproject article isnt what I am looking for though. I would like to send and return a continuous, unbroken stream – tinonetic Mar 22 '15 at 14:42
  • Hello. What do you exactly mean by continuous stream? Like if you are streaming live video for example? Iam using stream to transfer big files - client chooses file to transfer, hit "Upload" button and the whole file gets streamed to service side. I will update my answer in few minutes with some implementation I have. – JakubJ Mar 22 '15 at 17:00
  • I mean there is no break in the stream. As I stream in input, it gets processed, and returned on the go. Similar to the generally used method signature `void ProcessStream(Stream input, Stream output)` where `ProcessStream` processes and writes the processed `input` stream byte by byte to `output`. No `Stream.Close()` inside operation. This is fine in the context of one application. But entirely different with WCF. The catch is, with `transferMode=Streamed`, you can have one, and only one parameter(either a raw `Stream` or something composite within a `MessageContract`). – tinonetic Mar 22 '15 at 17:22
  • Oh I see...so you basically dont want to divide it to 3 steps: Upload (stop),Process data (stop),Download data, right? I have updated my post with some code I have, but I did not realized you want something else, than I have implemented. Iam afraid I dont know how to implement the scenario you need. – JakubJ Mar 22 '15 at 17:25