Friday 27 March 2009

Castle WCF Integration Facility

I work predominantly on a multi-tenant application which is experiencing an upsurge in sales.  The client has recently dropped the price significantly which has resulted in a rush of sales.

The model I have chosen, rightly or wrongly is for each customer to have their own website and database instance.  The obvious problem with this approach is rolling out releases and database schema updates.  In order to minimise the pain of a release, I have grouped certain functionality into discreet services that each customer website instance accesses via MSMQ. The obvious logic being that software updates are rolled out to one location and not 20 websites.

Using WCF to listen and pick messages of the MSMQ was an easy choice. A windows service is employed to host the service. In the past this is tedious code I have written manually.

Prior to this post, I have used svcutil.exe to generate my client proxies and xml configuration files which initially is not a problem but can be an irritating habit to acquire. A bigger problem I faced was how to incorporate some of my technology stack such as dependency injection into the windows service. I was very against going down the service locator route which is a concept I do not favour.

Enter the hero of the piece in the form of the impressive Castle WcfFacility. I use a lot of the castle stack as is, so this was the gateway to a seamless experience from my windows service. The WcfFacility facilitates Wcf configuration for both hosting a service and creating Wcf clients in the form of a fluent interface.

Finding an example of the configuration for hosting the service from a windows service drew a blank. I googled and searched through the excellent unit tests that are included in the castle source but nothing returned.

After much trial and error, coffee and profanity I eventually got there.

First up, the code to load the container in the windows service. This code is the body of the OnStart method of the windows service:

var returnFaults = new ServiceDebugBehavior{
                           IncludeExceptionDetailInFaults = true
                       };

var metadata = new ServiceMetadataBehavior {HttpGetEnabled = true};
var serviceModel = new DefaultServiceModel();

serviceModel.BaseAddresses.Add(new Uri("http://localhost:8080/Distribution/"));

_container = new WindsorContainer();

var binsorFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Windsor.boo");

BooReader.Read(_container, binsorFilePath);

_container.AddFacility<WcfFacility>().Register(
   Component.For<IDistributionProcessor>()
       .ImplementedBy<DistributionProcessor>()
       .ActAs(serviceModel.AddEndpoints(
                  WcfEndpoint.ForContract<IDistributionProcessor>()
                      .BoundTo(new NetMsmqBinding(NetMsmqSecurityMode.None)
                                   {
                                       UseActiveDirectory = false
                                   })
                      .At("net.msmq://localhost/private/distribution") //TODO: Move into configuration
                  ).Hosted()
       ),
      Component.For<IServiceBehavior>().Instance(returnFaults),
      Component.For<IServiceBehavior>().Instance(metadata)
    );

To start the service:

_serviceHost = (ServiceHost) new DefaultServiceHostFactory().CreateServiceHost(typeof (IDistributionProcessor).AssemblyQualifiedName, new Uri[0]);
_serviceHost.Open();


On the client side, I register the Wcffacility like so:

_container = new WindsorContainer().AddFacility<WcfFacility>().Register(
    WcfClient.ForChannels(
        new DefaultClientModel
        {
            Endpoint = WcfEndpoint.ForContract<IDistributionProcessor>()
                .BoundTo(new NetMsmqBinding(NetMsmqSecurityMode.None)
                {
                    UseActiveDirectory = false
                })
                .At("net.msmq://localhost/private/distribution") //TODO: Move into configuration
        })
    );


Job done!!

No comments:

Post a Comment