WCF sets the default message size really low. I think the reason they did this was to slap the face of all the lazy developers out there. Just to wake them up. A limit that low is almost guaranteed to be hit in development rather than production. That is the WCF team saying "SMACK! Pay attention, what you do here is very important to the performance of the app."

There are three ways of fixing this "problem".

  1. Increase the maximum message size This will only get you so far, and should only be used when two conditions apply. The first is that you know the maximum size a message can be, and the second is that that maximum size multiplied by the maximum number of simultaneous messages can fit in the lowest amount of memory that your service application can run with.
  2. Chunk the message into smaller messages This is especially a good solution when ordering is not needed. For example if I was going to send my CD collection over the wire I might chose to chunk it. If you happen to get the Beatles before ABBA (cough, um… that one must be my wife’s CD) it is no big deal. However if you were to chunk a file, there could be problems because if chunk #3 arrived before chunk #2 you now have a corrupted file.
  3. Turn on streaming This one should really only be used if you are simply copying that network stream to some other input / output device, like your monitor or a hard drive. To do this means you have to have a special contract which takes a stream as the single message body.

Let’s take a simple file storage service as an example. The Store file could have a single Stream parameter, but what about the other information that needs to be passed along with the Stream, like the name and author? The contract will probably end up looking something like this:

	[MessageContract]
	public class FileMessage
	{
		[MessageHeader]
		public string fileName;

		[MessageBodyMember]
		public Stream fileStream;

		public FileMessage()
		{
		}

		public FileMessage(string fileName, Stream fileStream)
		{
			this.fileName = fileName;
			this.fileStream = fileStream;
		}
	}

	[ServiceContract]
	public interface IFileService
	{
		[OperationContract]
		void StoreFile(FileMessage fileMessage);
		[OperationContract]
		Stream GetFile(string fileName);
	}

When you go to implement the contract people often wonder when to close the stream. The advice given in "Programming WCF Services" is a little confusing (and not entirely correct) around this topic. Page 204 states:

When the client passes a request stream to the service, the service may read from the stream long after the client is gone. The client has no way of knowing when the service is done using the stream. Consequently the client should not close the stream— WCF will automatically close the client-side stream once the service is done using the stream.

A similar problem exists when the client interacts with a response stream. The stream was produced on the service side, and yet the service does not know when the client is done using the stream, nor can WCF help, because it has no idea what the client is doing with the stream. The client is always responsible for closing reply streams.

The first paragraph is not really correct because if the client is calling the service synchronously then it knows exactly when to close the stream. It turns out that the second paragraph is the only case when you don’t close the stream. So really to summarize – Always close the stream, except when you don’t know when to close it. The only time that is true is when the stream is being returned from the service and you don’t know when it has finished being written into the outgoing buffer. Here is the code first on the client side:

using (var fs = new FileStream(uploadPath, FileMode.Open, FileAccess.Read))
{
	client.StoreFile(new FileMessage(uploadPath, fs));
} // NOTE: the stream is closed here
...
using (Stream s = client.GetFile(downloadFileName))
{
	// do something with the stream
} //NOTE: the stream is closed here

Then on the service side:

public Stream GetFile(string fileName)
{
	// this is the only place that we don't close the stream
	// WCF does it for us in this case, whenever it is done sending the message
	return new FileStream(fileName, FileMode.Open);
}

public void StoreFile(FileMessage msg)
{
	// do something with the stream
	msg.fileStream.Close();
}

No comments yet.

No Comments »

You must be logged in to post a comment.