Reputation: 8034
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:
I would like to know why the generated service operation does not have a return type when I specified it in the contract?
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 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();
Upvotes: 5
Views: 5005
Reputation: 235
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();
}
}
Upvotes: 2