Griffin.Container – “The drunken IoC container”

(I started writing this blog entry at friday night, but somehow I didn’t manage to finish it.)

My son has just fallen asleep and wifey is working. Which means that I got a friday night all by myself. So I’ve put some beer and Bacardi Breezers (kinda girly, I know. But I can’t help myself) in a nice row on my desk. Instead of continuing to code on my next entrepenural endeavor I’ve decided to do something fun and challenging. And that is to code an inversion of control container in three hours (until the boss get’s home). (It took another two hours today to clean up the nonsense in this blog entry)

It’s my third attempt during the last couple of years. My both last attempts was just a bit of fooling around and this time I’ve decided to try to create a lean killing machine. So I’ve come up with a slightly different approach compared to my last attempts.

The responsibility

In my eyes an IoC container has three responsibilites. Hence I’ve divided the container into three different classes.

Registration

A container got to be able to register the services that should be provided. I hate those XML configurations since it’s hard to discover configuration errors. I also cry when I have to use API’s which heavily uses extensions methods, since the intellisense for them is messy and hard to understand.

I’ve therefore created three types of registration options:

The attribute

Registration through attributes. Works quite nicely. Create a class and add the [Component] attribute on it. Repeat for all classes in all of your assemblies.

[Component]
public class MyClass : IService
{
}

Control the lifetime:

[Component(Lifetime = Lifetime.Singleton)]
public class MyClass : IService
{
}

And register everything using:

registrar.RegisterComponents(HostingEnvironment.MapPath("~/bin"), "*.dll));

Done.

The module

You can also control the registration process through modules. Great since each module/namespace/section can itself decide what should be registered in the container.

public class UserManagementModule : IContainerModule
{
    public void Register(IContainerRegistrar registrar)
    {
        registrar.Register<UserService>();
    }
}

Then just load all modules:

// can also specify path + filepattenr as the example above.
registrar.RegisterModules(Assembly.GetExecutingAssembly());

The traditional approach

There are also the traditional RegisterXxxxx methods.

public interface IContainerRegistrar
	IEnumerable<ComponentRegistration> Registrations { get; }

	void RegisterComponents(Lifetime defaultLifetime, string path, string filePattern);
	void RegisterComponents(Lifetime defaultLifetime, params Assembly[] assemblies);

	void RegisterModules(string path, string filePattern);
	void RegisterModules(params Assembly[] assemblies);

	void RegisterConcrete<TConcrete>(Lifetime lifetime = Lifetime.Scoped) where TConcrete : class;
	void RegisterConcrete(Type concrete, Lifetime lifetime = Lifetime.Scoped);

	void RegisterService<TService>(Func<IServiceLocator, object> factory, Lifetime lifetime = Lifetime.Scoped);
	void RegisterService(Type service, Func<IServiceLocator, object> factory, Lifetime lifetime = Lifetime.Scoped);

	void RegisterType<TConcrete, TService>(Lifetime lifetime = Lifetime.Scoped)
		where TService : class
		where TConcrete : class;
	void RegisterType(Type concrete, Type service, Lifetime lifetime = Lifetime.Scoped);

	void RegisterInstance<TService>(TService instance) where TService : class;
	void RegisterInstance(Type service, object concrete);
}

The build plan

Performance is important. And you can’t get performance if you do not analyze how each service should be built (before the actual service location). I therefore go through all classes, check their dependencies and create a plan for how they should be built. I then analyze which classes is used by which services and create a mapping between each service and the implementing classes.

The builder supports multiple constructors. It uses the most specific constructor for which it can resolve all dependencies for.

/// <summary>
/// Used to build the container.
/// </summary>
public interface IContainerBuilder
{
    /// <summary>
    /// Builds a container using the specified registrations.
    /// </summary>
    /// <param name="registrar">Registrations to use</param>
    /// <returns>A created container.</returns>
    IParentContainer Build(IContainerRegistrar registrar);
}

Service location

The container itself IS implementing the service location pattern. Hence the container interface is really a service location interface and we’ll use that as our base.

public interface IServiceLocator
{
    bool IsRegistered(Type type);
    T Resolve<T>() where T : class;
    object Resolve(Type service);
    IEnumerable<T> ResolveAll<T>() where T : class;
    IEnumerable<object> ResolveAll(Type service);
}

The container itself have been divided into two containers. A parent container which is the one generated by the builder and a scoped container.

public interface IParentContainer : IServiceLocator
{
    IChildContainer CreateChildContainer();
}

public interface IChildContainer : IServiceLocator, IDisposable
{
    
}

Scoped services can only be resolved by a scoped container. So you got to use something like this:

using (var scoped = container.CreateChildContainer())
{
    // All scoped services which is resovled here
    // will be disposed automatically.
}

That should make it easier to handle scoping in WCF, MVC3 and similar frameworks.

Extension points

The divided architecture allows you to customize the container quite nicely.

Custom registration

  • Inherit ContainerRegistrar and override Add to be able to handle all registrations.
  • Override CreateRegistration to control which interfaces a class can implement
  • Pass a custom IServiceFilter class to the ContainerRegistrar to filter out which service all classes can be registered for.

Building

  • Inherit ContainerBuilder and override TryGetConstructor to be able to control which constructor the builder should use.

Service location

  • Inherit Container and ChildContainer and override the GetInstance method to get full control over the returned instances. Perfect approach if you want to generate proxies or use AOP.

Usage

public class IntegrationTests
{
    [Fact]
    public void SimpleRegistrationTransient()
    {
        var registrar = new ContainerRegistrar();
        registrar.RegisterType<MySelf>(Lifetime.Transient);
        var builder = new ContainerBuilder();
        var container = builder.Build(registrar);

        var instance1 = container.Resolve<MySelf>();
        var instance2 = container.Resolve<MySelf>();

        Assert.NotSame(instance1, instance2);
    }


    [Fact]
    public void OneDependencySingleton()
    {
        var registrar = new ContainerRegistrar();
        registrar.RegisterType<MySelf>(Lifetime.Transient);
        registrar.RegisterType<OneDepencency>(Lifetime.Singleton);
        var builder = new ContainerBuilder();
        var container = builder.Build(registrar);

        var instance1 = container.Resolve<OneDepencency>();
        var instance2 = container.Resolve<OneDepencency>();

        Assert.Same(instance1, instance2);
    }

    [Fact]
    public void ResolveAllStartable()
    {
        var registrar = new ContainerRegistrar();
        registrar.RegisterType<Startable1>(Lifetime.Singleton);
        registrar.RegisterType<Startable2>(Lifetime.Singleton);
        var builder = new ContainerBuilder();
        var container = builder.Build(registrar);

        var instances = container.ResolveAll<ISingletonStartable>();

        Assert.Equal(2, instances.Count());
    }

    [Fact]
    public void ResolveFromComponents()
    {
        var registrar = new ContainerRegistrar();
        registrar.RegisterComponents(Lifetime.Singleton, Assembly.GetExecutingAssembly());
        var builder = new ContainerBuilder();
        var container = builder.Build(registrar);

        container.Resolve<OneDepencency>();
    }

    [Fact]
    public void ResolveFromModules()
    {
        var registrar = new ContainerRegistrar();
        registrar.RegisterModules(Assembly.GetExecutingAssembly());
        var builder = new ContainerBuilder();
        var container = builder.Build(registrar);

        container.Resolve<MySelf>();
    }

    [Fact]
    public void ChildContainer()
    {
        var registrar = new ContainerRegistrar();
        registrar.RegisterType<MySelf>();
        var builder = new ContainerBuilder();
        var container = builder.Build(registrar);

        MySelf instance;
        using (var childContainer = container.CreateChildContainer())
        {
            instance = childContainer.Resolve<MySelf>();
        }

        Assert.True(instance.IsDisposed);
    }
}

Performance

I used the benchmark testing code which have been created by Daniel Palme.

Chart:

(I had to remove ninject & spring.net from the chart since they are way too slow)

The performance is OK by me since I’ve not done any optimizations yet.

Summary

I’m quite satisfied with the result and a bit suprised that I managed to create the architecture that I did last night. It was really fun and I think that I’ll continue to improve the container so it becomes even more useful.