Recently I posted an article and some code for a service which logs onto MSN Messenger and exposes some web services for sending messages. I promised in that article that if I got time to refactor the code to use WCF I would post it - here it is and the code is quite a lot simpler. The revised code is attached to this blog post, you can click here to download the code and binaries.
Removing the Web Services Code
There was previously some complexity in hosting the web services and ensuring that they were functioning correctly. This complexity manifested itself in the form of a background Thread to service requests, a ManualResetEvent for synchronisation, two AppDomains at runtime and a few helper classes. Since we are no longer using web services, all of this can be deleted.
Creating a Service Contract
Often some thought is need around the exact operations to be exposed and their various signatures. However, since I wasn't aiming to change functionality at all, I didn't see any point in revisiting the operation signatures we had previously. So all that was involved in creating the service contract was setting up an IMessengerService interface and decorating it with the appropriate attributes:
public interface IMessengerService
bool QueueMessage(string recipients, string message);
bool QueueMessageToOnePerson(string recipient, string message);
bool QueueMessageToOnePersonWithValidity(string recipient, string message, int validityInMinutes);
bool QueueMessageWithValidity(string recipients, string message, int validityInMinutes);
Hosting the WCF Service
The old web service methods were thin wrappers around calls to QueueManager methods. These wrapper methods now live in the QueueManager class which implements IMessengerService. So all that's needed to expose this singleton instance over WCF is the following code:
_host = new ServiceHost(QueueManager.Instance);
and an attribute on the QueueManager class as follows:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class QueueManager : IMessengerService
// Details of the class omitted here
Reworking the MSBuild Client
Strangely enough, while the move to WCF was a simplification of the service code there is now more code in the MSBuild client. This is due to one particular quirk of code running as an MSBuild task - there is no config file. WCF is (rightly so) tailored to expressing service endpoints in config files.
As a result of this, the MsnNotification task has to programmatically construct the full endpoint (including binding, service behaviours, etc) based on parameters passed in from the build script. To avoid excessive complexity, the code deduces the transport protocol from the service URL and then uses the default binding.
Binding binding = null;
EndpointAddress address = new EndpointAddress(_url);
binding = new BasicHttpBinding();
binding = new NetTcpBinding();
binding = new NetMsmqBinding();
binding = new NetNamedPipeBinding();
binding = new NetPeerTcpBinding();
Log.LogError("Unable to deduce correct binding from URL. Supported schemes are http, https, net.tcp, net.msmq, net.pipe and net.p2p");
ChannelFactory<MessengerSvc.IMessengerServiceChannel> factory = new ChannelFactory<MessengerSvc.IMessengerServiceChannel>(binding, address);
MessengerSvc.IMessengerServiceChannel channel = factory.CreateChannel();
// Use the service proxy as before
Benefits of Using WCF
So aside from the use of technology, have we gained anything from using WCF instead of the web services? Sure we have. Here's the list as I see it:
- Reduced code complexity - a lot of the complex stuff around the hosting of the web services is now taken care of by the System.ServiceModel.ServiceHost class.
- Better scalability - the sharp-eyed reader will notice that previously we only had a single thread to process incoming requests. The ServiceHost class does a better job of servicing clients.
- A wider range of protocols - we are no longer limited to plain SOAP messages. We can declaratively add in things like security, transactions, reliable messaging, etc, etc (although the MSBuild task doesn't support these right now). Additionally, we can expose the service over MSMQ, TCP sockets, etc, etc.
Howard took one look at my code and immediately started refactoring it. The updated version is attached to this post. The main change he made was to improve the access to the configuration file - creating a class that derives from ConfigurationSection is much more of a .Net 2.0 way of doing things.