Castle Windsor and WCF: A Match Made in Heaven

Tue, Apr 1, 2008 6-minute read
(Note: I've been out for a bit due to new recent additions to my life. The first was a (third) daughter born 2 weeks early and the second is an aggressive vasectomy schedule. Too much information, you say? I agree.) If you've read any of my other posts, you know I dig Castle Windsor. It's cool and allows a "special" developer like me to look pretty smart without actually being all that smart. I also really like WCF, as I feel it lead the charge of "things Microsoft started getting right", again helping me look smart by abstracting all the WS-*** and other service gobblety-gook out of my life. Since I REALLY like both of these technologies, I was hesitant to put them together, fearing I couldn't take the shear joy it would create. However, Ayende and Craig have created the WCF Facility, allowing me to register my services on the Windsor container, and thus use dependency injection to handle all the stuff my services need. This post is a (hopefully) quick "how to" on this really cool facility. (Note: This is my first foray into using Binsor for Windsor Configuration. All the smart guys were using it while I was still XMLing in the dark ages, so I had to follow suit.)

Step Uno: Write a Service

Let us create a simple WCF service. It looks like:

[ServiceContract()]

public interface IService1

{

[OperationContract]

string MyOperation1(string myValue);

[OperationContract]

void ThrowError();

}

public class Service1 : IService1

{

private ILogger logger; //Yes, yes, should init to a NullInstance.

public ILogger Logger

{

get { return logger; }

set { logger = value; }

}

#region IService1 Members

public string MyOperation1(string myValue)

{

logger.Info("MyOperation1 called with {0}",myValue);

return "Hello: " + myValue;

}

public void ThrowError()

{

throw new Exception("AAAAAAH!");

}

#endregion

}

So, you see the service contract, you see the implementation. Please notice the following details:
  • I added an ILogger property. This is a dependency.
  • There is a method that just ralphs an error. Usually I am not as obvious with my errors, but at least I will be able to debug this one.
Usually, at this point, you'd open up the web.config file and start writing mountains of <service> tags and <behaviors> and all that. We aren't going to do that. We are going to bring in Windsor to take care of all our needs. Yay!

Bring in da Noise, Bring in da Windsor

I am going to presume that you, the reader, knows about Windsor and, at least at a high level, how to configure it. Basically, for a web app (we are gonna host our services in IIS) you need to:
  • Write the configuration files for Windsor, consisting of properties, facilities, and components. Go read the castleproject.org site for some decent examples.
  • Register the container at startup.
As I stated previously, I am using Binsor to write my config files. (Tangent: I need to learn Boo. Ayende, is that book done yet?) Here goes:

import System;

import System.Reflection

import System.ServiceModel

import System.ServiceModel.Description from System.ServiceModel

import Castle.Facilities.Logging

import Castle.Facilities.WcfIntegration

import Ruprict.Grok.Castle.WcfIntegration.Core

facility LoggingFacility:

loggingApi = LoggerImplementation.Log4net

configFile = 'log4net.config'

facility WcfFacility

component 'windsor.service', IService1, Service1:

ServiceModel = WcfServiceModel().Hosted() \

.AddEndpoints(WcfEndpoint.BoundTo(BasicHttpBinding()))

component 'error.handler',IServiceBehavior, LogExceptionHandler

component 'metadata.behavior',IServiceBehavior, ServiceMetadataBehavior:

HttpGetEnabled = true

This is Binsor 2.0 syntax, which is (from what I understand) yummier than ever. I do like it. It's compact and readable and I don't end up getting errors b/c I forgot to close a tag. Looking at the import statements, you see I bring in whatever namespaces I need, including my service namespace and any of it's dependencies. Here, I've yanked in the ServiceModel stuff from WCF so I can hook up my IServiceBehavior extension classes, as well as some other Castle Facility namespaces (namely, the Logging facility). Then, I start defining things. I grab the LoggingFacility from Castle, telling it I am using Log4Net and pointing it at the right config file. Then it's on to the star of our show, the WcfFacility. It seems odd that it just takes two words to bring in a facility that does so much. Ahh, simplicity, you are truly beautiful. Anyhoo, the first component is my WCF service (line 16) where you can see that I add a singular endpoint, bound to the BasicHttpBinding, um, binding. (NOTE: If you are using the BasicHttpBinding, like I am here, you don't even have to specify a service model, as the facility will deduce it from the base address.) Finally, I add 2 more components, both IServiceBehaviors. For those that don't know, IServiceBehavior is a way to extend WCF by create custom service (there are also endpoint and operation behaviors, all of which are treated the same by the WcfFacility) behaviors. In our example, I have created a LogExceptionHandler that will log errors to a log file. I also used a WCF core service behavior, ServiceMetatadataBehavior so I can use an HTTP GET to look at the service WSDL. With the configuration defined, we have to get our IIS web app to use it, right? That's done in the Global.asax by putting some code in the HttpApplication.Application_Start event.

public class Global : HttpApplication, IContainerAccessor

{

private static IWindsorContainer container;

protected void Application_Start(object sender, EventArgs e)

{

container = new WindsorContainer().Install(BinsorScript.FromFile("windsor.boo"));

}

protected void Application_End(object sender, EventArgs e)

{

container.Dispose();

}

#region IContainerAccessor Members

public IWindsorContainer Container

{

get { return container;

}

#endregion

}

That single line in Application_Start tells Windsor that I am using Binsor and the name of my config file. Gorgeous! Now, in order to host a WCF service in IIS, you need a .svc file as an endpoint, and (usually) web.config configuration sections out the yin-yang. We still need the former, pointing it to our WindsorServiceHostFactory and using the name from our Binsor config file.

<%@ServiceHost Language="C#"

Service="windsor.service" Factory="Castle.Facilities.WcfIntegration.WindsorServiceHostFactory"

%>

Now, when we crank up the application, the Windsor Container will attach the IServiceBehaviors in the config to our services, automatically wiring up my LogExceptionHandler and ServiceMetadataBehavior. This is especially sexy when you have multiple services in a single application and they ALL get the behaviors. Furthermore, since my service has an ILogger property (remember that?) and I am also using the LogFacility, it will create my Log4Net logger and give my service an instance. POW! BLAM! KABLOOEY! This is how we do it. I was going to put some examples of the service getting called using WcfTestClient.exe (free with .NET 3.5) so you could see my error behavior doing it's thang as well as prove to you that we don't need no stinking web.config sections, but I find them superfluous in a post that already is larger than I wanted it to be. If you have more questions, hit me with a comment. So, in summary, the keypoints here are:
  • Windsor rocks, and the WcfFacility is all that and a bag of chips.
  • You can have web.config <service> section-less WCF services, complete with auto-wired behaviors and dependencies.
  • Ayende and Craig are really, really smart.

More Examples

These two posts (here and here) in the Castle Dev Google Group go through some more examples from Craig. You'll even get to see me asking very basic questions. Also, Craig has promised he will post some stuff focusing on the client side of things, which should be very interesting. Update: Another, example of the newer (2008/12/02) stuff here.
Reblog this post [with Zemanta]