Multiple Streaming in WCF

What I have?

I have a WCF contract that accepts a Stream object.

What I need?

I want to convert the contract to accept multiple streams. But there is a problem with passing multiple streams to WCF. Now I have two options:

(1) Call WCF method multiple times

(2) Modify the contract to accept a 2D byte array with all the contents of the file

Can someone tell me which option is better?

Thanks in advance!

+2


source to share


4 answers


We solved this problem by building a wrapper implementation of Stream on the client that concatenates the list of streams together with each other (e.g. .Read delegates to first stream - when it hits EOF on stream 1, it starts reading stream 2, etc.) ... It has a metadata property that has the names of streams and their positions, which we include in the call in the [MessageHeader] arg attribute that says what in the stream and offsets of each (client streams must support .Length). The consumer of streams on the server knows how to read this metadata object and list the streams in order. The only limitation is that they must be processed in order. Works great!



+7


source


In WCF, you cannot have multiple stream parameters in one service method. It's just a systemic limitation.

See MSDN documentation for WCF Streaming :

Streaming restrictions

Using streaming mode introduces additional runtime restrictions.

Operations that occur via streaming transport can be contracted with at most one input or output parameter. This parameter matches the entire body of the message and must be a message derived from type Stream, or an IXmlSerializable implementation. Having a return value for an operation is equivalent to having an output parameter.



So now you can just call your method multiple times.

Mark

+4


source


On my system, I need to send a FileStream and a generic custom data list to a WCF service. This is how I do it to combine both into one stream and send it to a service that returns a stream of result to the client:

Service contract:

[ServiceContract]
[XmlSerializerFormat]
public interface IMyService
{
    [OperationContract]
    ResultClass MyOperation(Parameters param);
}

      

Return flow:

[MessageContract]
public class ResultClass
{
    [MessageBodyMember]
    public System.IO.Stream Stream { get; set; }
}

      

Input stream:

[MessageContract]
public class Parameters
{
    [MessageHeader]
    public long Length1 { get; set;}
    [MessageHeader]
    public long Length12 { get; set;}
    [MessageBodyMember]
    public Stream Stream { get; set;}
}

      

WCF client has:

public static void BuildStream(string filePath, List<CustomData> otherParams, ref Parameters param)
{
        BinaryFormatter formatter = new BinaryFormatter();
        FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        MemoryStream allStreams = new MemoryStream();
        MemoryStream dataStream = new MemoryStream();
        formatter.Serialize(dataStream, otherParams);
        dataStream.Seek(0, SeekOrigin.Begin);
        fs.Seek(0, SeekOrigin.Begin);
        fs.CopyTo(allStreams);
        dataStream.CopyTo(allStreams);
        param.TemplateLength = fs.Length;
        param.DataLength = dataStream.Length;
        param.Stream = allStreams;
        param.Stream.Seek(0, SeekOrigin.Begin);
}

      

In the service implementation, pull the first and second streams from the combined stream:

List<CustomData> dataSheets;
using (MemoryStream ms = new MemoryStream())
{
    MemoryStream dataStream;
    BinaryFormatter formatter = new BinaryFormatter();
    byte[] templatebuffer, databuffer;
    filebuffer = new byte[m_param.Length1];
    databuffer = new byte[m_param.Length2];
    // Get the whole stream
    m_param.Stream.CopyTo(ms);
    ms.Seek(0, SeekOrigin.Begin);
    // Get the first stream
    ms.Read(templatebuffer, 0, (int)m_param.TemplateLength);
    // Get the custom data
    ms.Read(databuffer, 0, (int)m_param.DataLength);
    using (dataStream = new MemoryStream(databuffer))
    {
        dataStream.Seek(0, SeekOrigin.Begin);
        dataSheets = (List<DataSheet>) formatter.Deserialize(dataStream);
        m_param.Stream.Close();
    ...

      

+1


source


You can consider MTOM binding and have a contract with several properties byte[]

.

0


source







All Articles