My organization moves and transforms a lot of data around, in a rather interesting problem space. As a result, we have a great number of complex service applications doing a variety of different tasks. The status quo was to recreate all of the plumbing that went into building new applications, such as creating worker threads, hooking them up to Windows service control mechanisms, start/stop procedures, etc. These constructs would always require some awareness of the specifics of the logic being written to do the real work, as it made for more convenient deployment.
Dependency Injection as a Solution
That never resonated very well with me. In the back of my mind, a simplistic but powerful adage led me to reject the standard operating procedure and search for something better: Only write the code that only you can write.
This is where dependency injection (wiki) would come into play. For the uninitiated, dependency injection is a design pattern that stresses instantiation and configuration of a dependency outside of the class that uses it, rather than making the class responsible for it. An example would be if a class were to utilize a data provider. Rather than loading a configuration file, and invoking the factory to create an instance of that provider, the class would merely expect that something else took care of all that, and injected a reference to the dependency instance into the primary class.
Good DI frameworks provide the ability to define dependencies by name as well as implemented type, and also should provide a variety of lifespans for the objects that are resolved (at a minimum, a DI framework should allow for creating new instances, and for managing singleton-like objects that are shared among consumer classes). The way of dependency management in .NET — when dependency injection is not the pattern of choice — involved having to define your own configuration types, and also having to implement logic to load and populate classes from configuration data. This made for a lot of boilerplate code that was repeated over and over again, and also an unbearable amount of typing and maintenance to make configuration sections and elements a reality!
Making It Happen
My framework of choice is Microsoft’s Unity (not to be confused with the graphics framework bearing the same name). I don’t really have any reason for picking that over, say, NInject, other than it suited my needs and turned out to be easy to work with. I’m not going to use this space to provide a step-by-step on how to use Unity. Microsoft has provided plenty of material to that end, and has done a far better job than I could (links are provided at the end of this post). Rather, I will use this space to provide some insight into how it can be utilized to maximize code reuse in your application management code.
The key to this is to implement a common contract for any worker in your application, bind each concrete worker to that contract in your DI configuration, and resolving all your workers at runtime in whatever code is controlling your application.
public interface IWorker
{
void Start();
void Stop();
}
public class WorkerA : IWorker
{
public WorkerA(IDependencyA dependencyA)
{
_dependencyA = dependencyA;
}
IDependencyA _dependencyA;
public void Start()
{
}
public void Stop()
{
}
}
public class WorkerB : IWorker
{
public WorkerB(IDependencyB dependencyB)
{
_dependencyB = dependencyB;
}
IDependencyB _dependencyB;
public void Start()
{
}
public void Stop()
{
}
}
In this rather simple example, you’ve got a contract, IWorker
, that implements just two methods — Start
and Stop
. WorkerA
and WorkerB
are both concrete implementations of IWorker
, each firing off some worker thread to do work, and each having its own separate dependency. These workers can be defined in a Unity configuration, which will provide a specification for managing instances and dependencies.
<register type="IWorker" name="A" mapTo="WorkerA">
<lifetime type="ContainerControlledLifetimeManager"/>
<constructor>
<param name="dependencyA" dependencyType="IDependencyA"/>
</constructor>
</register>
<register type="IWorker" name="B" mapTo="WorkerB">
<lifetime type="ContainerControlledLifetimeManager"/>
<constructor>
<param name="dependencyB" dependencyType="IDependencyB"/>
</constructor>
</register>
<register type="IDependencyA" mapTo="SomeConcreteA" />
<register type="IDependencyB" mapTo="SomeConcreteB" />
This particular configuration maps two IWorkers
, WorkerA
and WorkerB
, and informs the DI framework on how to construct them at runtime. We intend these to be singleton instances, which is why the <lifetime/>
is defined as “ContainerControlledLifetimeManager
.”
With this set up, managing workers in an application is a trivial task. You could plug this into a service controller to start the workers:
var workers = UnityContainerSingleton.Instance.Container.ResolveAll<IWorker>();
foreach (var worker in workers) {
worker.Start();
}
And to stop them:
var workers = UnityContainerSingleton.Instance.Container.ResolveAll<IWorker>();
foreach (var worker in workers) {
worker.Stop();
}
(NOTE: I’m calling an imaginary singleton that contains a reference to a Microsoft.Practices.Unity.UnityContainer
instance. If you don’t put your container into a singleton, and instead try to load it from configuration every time you need it, you are going to have a bad time. Trust me).
Why Go Through All This?
It may seem like there are a bunch of moving parts, but it really is worth it to make use of dependency injection in your applications. For us, it has made the development and deployment of Windows services very simple. We can use the same service application code over and over, merely changing out the configuration files to construct entirely different apps. The service application has no awareness of any of the libraries containing the business logic, and as long as anyone on the team implements proper IWorkers
, and follows two basic rules:
Start()
must be non-blocking. Stop()
must be blocking until the class’ worker is finished.
Everything else is pretty much taken care of. YMMV, but in our particular case, new server apps can be spun up and deployed in a matter of hours, and we can even mix and match different worker configurations within the same application domains if we choose. It has enhanced our development, and sped up time to delivery, and hopefully the pattern applied in this manner can do the same for someone else.