In my spare time over the last week or so I added a very simple IoC container abstraction to ProjectExtensions.Azure.ServiceBus. Incidentally, if you are interested in building scalable applications that use a service bus, the ProjectExtensions version is a great lightweight choice whether your application lives on Azure or not. The Azure Service Bus API is fairly simple itself until you start dealing with tricky bits like transient faults and the need to poll intelligently to pick up new messages. The ProjectExtensions library takes care of all the nasty details so all you have to do is put messages on the bus and define classes to consume them. Although it does not have nearly all the features of NServiceBus, it doesn’t have all the complexity either. I should also note that the Azure service bus is easy to configure, very fast and extremely inexpensive. In many ways, it is even a better choice than MSMQ for applications that live outside Azure if you want your application to be easy to deploy and manage. Anyway, enough digression; I’m here to talk a little about the IoC container abstraction I put together for the ProjectExtensions Azure bus project.
Well, that’s not exactly true either. I’m not going to talk about the implementation. There’s nothing particularly interesting, complex or tricky about it. If you don’t believe me, go look at it on Github. I’ll wait. Boring huh? Anyway, many open source projects implement a minimal application-specific IoC abstraction. On the service bus side, you can find IoC abstractions in both NServiceBus and Rhino Service Bus. I would guess MassTransit has one too. Does anyone use MassTransit anymore? Geez, I’m digressing again. What was my point?
Ahh, I remember now. It sure was easy to put together a simple IoC abstraction for ProjectExtensions.Azure.ServiceBus because at their core all five popular IoC containers I used, Autofac, Castle Windsor, Ninject, Structure Map and Unity, have similar capabilities, similar APIs and perfectly adequate performance. Certainly, they all have slightly different philosophies and, when you dig deep, significant differences in their APIs. I am simply not the right guy to get into those details here. I am fairly expert with Castle Windsor and Autofac, but knew very little about the others until a couple days ago. If you want an expert, in-depth analysis, along with some great insight into how and why to use DI/IoC, go get a copy of “Dependency Injection in .NET” from Manning Press. My only intent here is to point out that they all work and they all work well for the basic use cases in ProjectExtensions.
So what does ProjectExtensions do with the IoC? Well, it’s as simple as the following:
/// <summary>
/// Generic IOC container interface
/// </summary>
public interface IAzureBusContainer {
/// <summary>
/// Resolve component type of T with optional arguments.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T Resolve<T>() where T : class;
/// <summary>
/// Resolve component with optional arguments.
/// </summary>
/// <param name="t">The type to resolve</param>
/// <returns></returns>
object Resolve(Type t);
/// <summary>
/// Register an implementation for a service type.
/// </summary>
/// <param name="serviceType">The service type.</param>
/// <param name="implementationType">The implementation type.</param>
/// <param name="perInstance">
/// True creates an instance each time resolved.
/// False uses a singleton instance for the entire lifetime of the process.
/// </param>
void Register(Type serviceType, Type implementationType, bool perInstance = false);
/// <summary>
/// Registers the configuration instance with the bus if it is not already registered
/// </summary>
void RegisterConfiguration();
/// <summary>
/// Build the container if needed.
/// </summary>
void Build();
/// <summary>
/// Return true if the given type is registered with the container.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
bool IsRegistered(Type type);
}
Pretty simple huh? Given the requirement to support any IoC, this kind of interface works pretty well. It gives us everything we need to allow library users to leverage the IoC container of their choice without making our lives too difficult. Because the interface is very simple, it is also quite easy for library users to roll their own support for any IoC they use that we don’t happen to support ourselves. If we settled for supporting only one container, we could have a far more elegant implementation. In fact, that’s what we had when we started with Autofac. However, consumers wanted to use the IoC of their choice with minimal fuss and this interface makes that possible.
There is one little twist left and that’s disposal of per-instance components that happen to implement IDisposable. Castle Windsor, for example, will hold onto disposable components until they are released. There are a couple ways to solve this. For example, some of the containers have the concept of a subcontainer that releases disposables when it goes out of scope. However, I want to keep this implementation simple so a little more investigation is needed. I’ll post the solution I settle on next time.
You must be logged in to post a comment.