Whoops, I accidentally got some link love from Phil Haack, but my blog has been dormant for 8 months! Time to start posting all those half-finished Windows Live Writer drafts! Here goes...
Say you've implemented a WCF service that uses a business logic layer which in turn uses a data access layer. Rather than hard-code these dependencies into your service's constructor, you would like to use dependency injection to enable unit testing of each layer using a mock object framework such as Rhino Mocks.
For our purposes we'll do constructor injection using Spring.NET, although I've also heard good things about the Castle containers, and ObjectBuilder from Microsoft patterns & practices looks promising.
So we have a few simple classes and interfaces that we'd like to automagically hook up:
[ServiceContract]
public interface IServiceContract
{
[OperationContract]
...
}
public class ServiceLayer : IServiceContract
{
IBusinessLogic _businessLogic;
public ServiceLayer(IBusinessLogic businessLogic)
{
_businessLogic = businessLogic;
}
...
}
public interface IBusinessLogic
{
...
}
public class BusinessLogic : IBusinessLogic
{
IDataAccess _dataAccess;
public BusinessLogic(IDataAccess dataAccess)
{
_dataAccess = dataAccess;
}
...
}
public interface IDataAccess
{
...
}
public class DataAccess : IDataAccess
{
...
}
We'd like to accomplish the equivalent of the following code whenever our service is created:
return new ServiceLayer(new BusinessLogic(new DataAccess()));
What does the Spring.NET equivalent look like?
IApplicationContext ctx = ContextRegistry.GetContext();
return (ServiceLayer)ctx.GetObject("ServiceLayer");
How does Spring.NET know how to wire up the dependencies? It uses reflection and config hints to determine who depends on whom:
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<object name="DataAccess" type="NamespaceA.DataAccess, AssemblyA" />
<object name="BusinessLogic" type="NamespaceB.BusinessLogic, AssemblyB" autowire="constructor" />
<object name="ServiceLayer" type="NamespaceC.ServiceLayer, AssemblyC" autowire="constructor" />
</objects>
</spring>
That's great, but how do we get WCF to do this for us at the right place at the right time? By using WCF's "behavior injection" functionality of course!
We'll need to implement an IInstanceProvider that allows us to serve up instances of our service each time WCF needs a new instance.
public class DependencyInjectionInstanceProvider : IInstanceProvider
{
private Type _serviceType;
public DependencyInjectionInstanceProvider(Type serviceType)
{
_serviceType = serviceType;
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
object result = null;
IApplicationContext context = ContextRegistry.GetContext();
string[] objectNames = context.GetObjectNamesForType(_serviceType);
if (objectNames.Length != 1)
{
throw new YourOwnException(
string.Format(
CultureInfo.InvariantCulture,
"There must exist exactly one <object> definition for the {0} service in the Spring configuration",
_serviceType.Name)
);
}
return context.GetObject(objectNames[0]);
}
public void ReleaseInstance(System.ServiceModel.InstanceContext instanceContext, object instance) { }
}
Then we'll need an IServiceBehavior that plugs in our IInstanceProvider at the right place at the right time. Remember, WCF could choose to activate our service per call, per private session, per shared session, or as a singleton.
public class DependencyInjectionServiceBehavior : IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
if (cd != null)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider =
new DependencyInjectionInstanceProvider(serviceDescription.ServiceType);
}
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) {}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {}
}
Now we want to actually use this behavior. You could implement classes to apply this behavior using custom attributes, via config, or programmatically via a custom ServiceHost. I chose to go the ServiceHost route.
public class MyServiceHost : ServiceHost
{
public MyServiceHost() : base() { }
public MyServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) { }
protected override void OnOpening()
{
this.Description.Behaviors.Add(new DependencyInjectionServiceBehavior());
base.OnOpening();
}
}
And of course a custom ServiceHost needs a custom ServiceHostFactory if you want IIS to host it (you can use your custom ServiceHost directly when self-hosting):
public class MyServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new MyServiceHost(serviceType, baseAddresses);
}
}
This factory can then be referenced from your .svc files:
<%@ ServiceHost
Service="NamespaceC.ServiceLayer, AssemblyC"
Factory="NamespaceD.MyServiceHostFactory, AssemblyD"
%>
Steve Maine has a great series of posts with more details on how service activation and hosting work: