Castle Windsor and WCF: A Match Made in Heaven

(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]
Advertisements

About Ruprict

I am a nerd that is a Nerd Wannabe. I have more kids than should be allowed by law, a lovely wife, and a different sense of humor than most. I work in the field of GIS, where I am still trying to find myself on the map. View all posts by Ruprict

6 responses to “Castle Windsor and WCF: A Match Made in Heaven

  • Robert Mircea

    I am trying to configure a WCF service using Binsor and the latest trunk of Castle and Rhino.Commons.

    I am using the following snippet to register service:
    component ‘svc’, IMyService, MyServiceImpl:
    ServiceModel = DefaultServiceModel().Hosted().AddEndpoints(WcfEndpoint.BoundTo(BasicHttpBinding()))

    component ‘metadata_behavior’,IServiceBehavior,ServiceMetadataBehavior:
    HttpGetEnabled = false

    When accessing the service, I receive an error message:
    The value could not be added to the collection, as the collection already contains an item of the same type: ‘System.ServiceModel.Description.ServiceMetadataBehavior’. This collection only supports one instance of each type.
    Parameter name: item

    How can I get rid of this error?

    2. How can I specify transport options for the WCF service? For example, I want to enable SSL + basic authentication.

    Thanks!

  • ruprict

    Hey Robert…you caught me on my vacation, so I’ll do my best to answer part of the question, then we’ll put it to the group.

    The error you are getting is due to the facility adding a ServiceDebugBehavior to all services by default. It will remove this behavior if you add a ServiceDebugBehavior (which it then adds back)

    Anyway, my wife is going to slap the computer out of my hands if I spend too much more time on this, so i posted your question on the Castle dev group (http://groups.google.com/group/castle-project-devel/browse_thread/thread/5c3c19414492e9da)

    For the SSL + basic auth stuff, the only way I know to do it with the WCF facility and binsor is to create the binding configuration in your web.config, then refer to it by name from the windsor.boo file….like so:

    Web.config:

    <system.serviceModel>
    <bindings>
    <basicHttpBinding>
    <binding name="SecurityByTransport">
    <security mode="TransportCredentialOnly">
    <transport clientCredentialType="Basic" proxyCredentialType="None" />
    </security>
    </binding>
    </basicHttpBinding>
    </bindings>
    </system.serviceModel>

    windsor.boo

    component ’svc’, IMyService, MyServiceImpl:
    ServiceModel = DefaultServiceModel().Hosted().AddEndpoints(WcfEndpoint.BoundTo(BasicHttpBinding(“SecurityByTransport”)))

    Note, I pass the binding config name to the BasicHttpBInding constructor.

    I am doing this from memory (again, vacation timer ticking) so I hope it helps.

    Lemme know if it doesn’t.

  • ppcanodehuelva

    Hi Robert,

    i am trying to get working the Logging facility on wcf services as you explained on the post, unfortunately the container never assigns the logging dependency to the ILogger property of the WCF service, on the other hand the ILogger property of a Behavior is correctly assigned.

    I am currently using the version on trunk version.

    I tried via svc and hosting the service by code. But there is no way to get it in both cases.

    I wonder whether it has been modified with the new version and the WcfFacility only assigns dependency via Behaviors to the service , or i miss something on the configuration.

    Ex:
    _container = new WindsorContainer();
    _container.AddFacility(“logging_facility”, new LoggingFacility(LoggerImplementation.Console));
    _container.AddFacility();

    _container.Register(
    Component.For().ImplementedBy(),
    Component.For().ImplementedBy()
    .ActAs(new DefaultServiceModel()
    .AddEndpoints(
    WcfEndpoint.BoundTo(ServiceConfiguration.Binding)
    .At(library.LogbookConf.ExplicitAddress)
    )
    .Hosted()
    )
    );

    new DefaultServiceHostFactory().CreateServiceHost(typeof(ILogbookContract).AssemblyQualifiedName, new Uri[0]);

    By the case: Logging facility is assigned to the InternalErrorHandlerSB, but not to the LogbookService.

    Many thanks in advance,
    Cheers.

    Pepe

  • ppcanodehuelva

    Regarding to the last post:

    The problem comes up when the ServiceBehavior has the InstanceContextMode.Single, for PerCall and PerSession work well.

    [ServiceBehavior(
    InstanceContextMode = InstanceContextMode.Single
    )]
    public class LogService

    regards

  • ruprict

    Sorry for the delayed response, took a couple of days to get thru holiday muck.

    So, I wonder if there is some weirdness if you make the InstanceContextMode=Single but don’t tell Windsor to manage the lifestyle as a singleton as well. Did you try setting the lifestyle of your service to singleton?

    A bit of a wild guess, but lifestyle management has bitten me more than once.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: