Adding behaviors to WCF services using Autofac

No Comments December 7, 2012

Autofac is a nice open source IoC container to use with WCF services because it provides nice integration capabilities.  For the most part, configuring and using the container is straightforward because the integration by default understands the scope of WCF requests, so there’s not much you have to do to manage the container.  Unless, that is, that you need to resolve an object outside of the normal scope of a request.  Applying service behaviors falls into that category.

I’m using a boilerplate service declaration with an .svc file that looks like the following:

<%@ ServiceHost Service="MyServices.IMyService, MyServices" Factory="Autofac.Integration.Wcf.AutofacServiceHostFactory, Autofac.Integration.Wcf" %>

It’s using the AutofacServiceHostFactory to create the service host, which is the crux of how WCF integration gets implemented with Autofac.  If we look at the source code for Autofac WCF integration (snippet below), we can see what is taking place in the CreateServiceHost() method of AutofacServiceHostFactory.

ServiceHost CreateServiceHost(IComponentRegistration registration, Type implementationType, Uri[] baseAddresses)
{
var host = CreateServiceHost(implementationType, baseAddresses);
host.Opening += (sender, args) => host.Description.Behaviors.Add(
new AutofacDependencyInjectionServiceBehavior(Container, implementationType, registration));

var action = AutofacHostFactory.HostConfigurationAction;
if (action != null)
{
action(host);
}

return host;

}

We can see that under the covers it is adding its own behavior called “AutofacDependencyInjectionServiceBehavior” before executing any action defined in AutofacHostFactory.HostConfigurationAction, which is useful in that it actually tells us exactly how to go about adding behaviors of our own.  Since the static HostConfigurationAction property is assignable at the time the container is built and the action delegate gives us a reference to the ServiceHost, we can attach anything we want to the ServiceHost.Opening event, just like Autofac does internally. 

But what if we need to actually resolve our behaviors at run-time instead of hardcoding them at design time?  This would be a nice way to inject different behaviors for different environments (QA, Prod, or mocks for unit tests), or give us some flexibility to turn behaviors on or off without a recompile.  To do this we just need to resolve the IComponentContext first, and then resolve the behaviors using the context.  We can also optionally only apply the behaviors to services that need them.  The example below shows how you might wire this up in your global.asax code. 

protected void Application_Start(object sender, EventArgs e)
{
var builder = new ContainerBuilder();

//Services
builder.RegisterType<MyService>().As<IMyService>();
builder.RegisterType<MyService2>().As<IMyService2>();

//Service Behaviors
builder.Register<MyBehavior>.As<IMyBehavior>();
builder.Register<MyBehavior2>.As<IMyBehavior2>();

//Load any overrides from web.config, these can override any registrations above
builder.RegisterModule(new ConfigurationSettingsReader());

//Build the container
AutofacHostFactory.Container = builder.Build();

//Register the action that will run after the factory creates the service host
AutofacHostFactory.HostConfigurationAction =
host =>
{
//this runs as soon as a service host is created and the AutofacDependencyInjectionBehavior is added
//we resolve the container
var ctx = AutofacHostFactory.Container.Resolve<IComponentContext>();

host.Opening += (sender, args) =>
{
//this runs when the service channel is opening
//add a behavior to any service
host.Description.Behaviors.Add(ctx.Resolve<IMyBehavior>());

//conditionally add a behavior if the service inherits from a type that requires it
if (typeof(MyServiceBaseThatRequiresIMyBehavior2).IsAssignableFrom(host.Description.ServiceType))
{
host.Description.Behaviors.Add(ctx.Resolve<IMyBehavior2>());
}
};
};
}

We build our container and register our services and behaviors first, then we register the Autofac ConfigurationSettingsReader module, which in turn registers any components we’ve defined in the web.config.  This is useful if we want to register default implementation components in code, and then override them in the web.config.  After the container is built, we set the HostConfigurationAction property.  Here I’m applying one behavior to all services, and another only to services that implement a certain base class (which could just as easily be an interface).


No Comments

Have a Comment?