Comparing .Net IoC containers, part five: Autofac

Tags: IocContainer, IocComparison, DependencyInjection, DI, IoC, code, autofac

I had not used Autofac before this test series, but it used on the Orchard blog engine project and has some interesting advanced features and was quite usable for the basic operations in the tests. The Autofac source code is under the MIT licence is on google code, and is written by Nicholas Blumhardt and other contributors. I have updated this blog post in June 2011 with new code for StructureMap 2, based on the comments by Brendan Forster.

Autofac requires all types to be registered, and creates a new instance each time by default.

With Autofac's syntax we are back in the more familiar terain of RegisterType<T>.As<U>(); and Resolve<T>. Note that the RegisterType<T> happens on a ContainerBuilder, and then a container is built from it which can Resolve. Like StructureMap, this emphasises that registration happens before use. The container is not actually immutable (in version 2 onwards), though it doesn't look like changing a container a lot after setup it is encouraged.

You can register using builder.RegisterType<T>().As<U>() or use the convenience methods: builder.RegisterType<T>().AsSelf() and builder.RegisterType<T>().AsImplementedInterfaces(). My opinion is that in the majority of cases RegisterType<T>().AsImplementedInterfaces() will do what you want e.g register FooService as IFooService, and have fewer moving parts (the registration won't change if the interface name changes). However, you may want to watch out for cases where it is not the best fit - e.g. the class implements a second interface such as IDisposable that you do not want registered. I can use builder.RegisterType<T>().AsSelf() syntax for most cases of the SweetVendingMachine and SweetShop - instances of thse concrete types are created, and they are injected with the other registered services. However when using a subclass of SweetShop, I have to fall back to builder.RegisterType<AniseedSweetShop>().As<SweetShop>()

A singleton is registered as:

builder.RegisterType<VanillaJellybeanDispenser>()
    .AsImplementedInterfaces().SingleInstance();

The syntax for a constructor parameter is quite good:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<AnyJellybeanDispenser>()
   .AsImplementedInterfaces()
   .WithParameter("jellybean", Jellybean.Lemon);

WithParameter takes a string and an object so as usual there is some loose typing at this point.

Registering an instance is

ContainerBuilder builder = new ContainerBuilder();
  builder.RegisterInstance(new AnyJellybeanDispenser(Jellybean.Cocoa))
   .AsImplementedInterfaces().SingleInstance();

and registering a factory method is

ContainerBuilder builder = new ContainerBuilder();
  builder.Register(c => new AnyJellybeanDispenser(Jellybean.Orange))
  .AsImplementedInterfaces();

The syntax for getting a list of instances is interesting - there is no ResolveAll() method. instead you make some registrations for type T, and then do a container.Resolve<IEnumerable<T>>() This is simple and logical - once you see it.

Autofac can also have modules like Ninject. Like Castle Windsor, it can register types from an assembly, and filter them to types that meet your criteria.

On the whole I found Autofac to be as easy, capable and modern design, and rich in features. it comes out as one of the best of the .Net IoC containers.

I have blogged about Autoregistration in Autofac.

Autofac: The Code

The code is maintained in a repository on Github. Here's the complete code for the tests:

namespace IoCComparison
{
    using System.Collections.Generic;
    using System.Linq;
    using Autofac;
    using NUnit.Framework;

    [TestFixture]
    public class AutofacTest
    {
        [Test]
        public void CanMakeSweetShopWithVanillaJellybeans()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<VanillaJellybeanDispenser>().AsImplementedInterfaces();
            builder.RegisterType<SweetVendingMachine>().AsSelf();
            builder.RegisterType<SweetShop>().AsSelf();
            IContainer container = builder.Build();

            SweetShop sweetShop = container.Resolve<SweetShop>();

            Assert.AreEqual(Jellybean.Vanilla, sweetShop.DispenseJellyBean());
        }

        [Test]
        public void CanMakeSweetShopWithVanillaJellybeansAlternateSyntax()
        {
            ContainerBuilder builder = new ContainerBuilder();
            // use the more precise but verbose registration syntax of builder.RegisterType<T>().As<U>();
            builder.RegisterType<VanillaJellybeanDispenser>().As<IJellybeanDispenser>();
            builder.RegisterType<SweetVendingMachine>().As<SweetVendingMachine>();
            builder.RegisterType<SweetShop>().As<SweetShop>();
            IContainer container = builder.Build();

            SweetShop sweetShop = container.Resolve<SweetShop>();

            Assert.AreEqual(Jellybean.Vanilla, sweetShop.DispenseJellyBean());
        }
        
        [Test]
        public void CanUpdateContainer()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<VanillaJellybeanDispenser>().AsImplementedInterfaces();
            builder.RegisterType<SweetVendingMachine>().AsSelf();
            IContainer container = builder.Build();

            // the container is not actually immutable, you can update it like this:
            ContainerBuilder updater = new ContainerBuilder();
            updater.RegisterType<SweetShop>().AsSelf();
            updater.Update(container);

            SweetShop sweetShop = container.Resolve<SweetShop>();

            Assert.AreEqual(Jellybean.Vanilla, sweetShop.DispenseJellyBean());
        }

        [Test]
        public void CanMakeSweetShopWithStrawberryJellybeans()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<StrawberryJellybeanDispenser>().AsImplementedInterfaces();
            builder.RegisterType<SweetVendingMachine>().AsSelf();
            builder.RegisterType<SweetShop>().AsSelf();
            IContainer container = builder.Build();


            SweetShop sweetShop = container.Resolve<SweetShop>();

            Assert.AreEqual(Jellybean.Strawberry, sweetShop.DispenseJellyBean());
        }

        [Test]
        public void JellybeanDispenserHasNewInstanceEachTime()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<VanillaJellybeanDispenser>().AsImplementedInterfaces();
            builder.RegisterType<SweetVendingMachine>().AsSelf();
            builder.RegisterType<SweetShop>().AsSelf();
            IContainer container = builder.Build();

            SweetShop sweetShop = container.Resolve<SweetShop>();
            SweetShop sweetShop2 = container.Resolve<SweetShop>();

            Assert.IsFalse(ReferenceEquals(sweetShop, sweetShop2), "Root objects are equal");
            Assert.IsFalse(ReferenceEquals(sweetShop.SweetVendingMachine, sweetShop2.SweetVendingMachine), "Contained objects are equal");
            Assert.IsFalse(ReferenceEquals(sweetShop.SweetVendingMachine.JellybeanDispenser, sweetShop2.SweetVendingMachine.JellybeanDispenser), "services are equal");
        }

        [Test]
        public void CanMakeSingletonJellybeanDispenser()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<VanillaJellybeanDispenser>().AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<SweetVendingMachine>().AsSelf();
            builder.RegisterType<SweetShop>().AsSelf();
            IContainer container = builder.Build();

            SweetShop sweetShop = container.Resolve<SweetShop>();
            SweetShop sweetShop2 = container.Resolve<SweetShop>();

            Assert.IsFalse(ReferenceEquals(sweetShop, sweetShop2), "Root objects are equal");
            Assert.IsFalse(ReferenceEquals(sweetShop.SweetVendingMachine, sweetShop2.SweetVendingMachine), "Contained objects are equal");

            // should be same instance
            Assert.IsTrue(ReferenceEquals(sweetShop.SweetVendingMachine.JellybeanDispenser, sweetShop2.SweetVendingMachine.JellybeanDispenser), "services are not equal");
        }

        [Test]
        public void CanMakeAniseedRootObject()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<AniseedSweetShop>().As<SweetShop>();
            IContainer container = builder.Build();

            SweetShop sweetShop = container.Resolve<SweetShop>();

            Assert.AreEqual(Jellybean.Aniseed, sweetShop.DispenseJellyBean());
        }

        [Test]
        public void CanUseAnyJellybeanDispenser()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<AnyJellybeanDispenser>().AsImplementedInterfaces()
                .WithParameter("jellybean", Jellybean.Lemon);

            builder.RegisterType<SweetVendingMachine>().AsSelf();
            builder.RegisterType<SweetShop>().AsSelf();
            IContainer container = builder.Build();

            SweetShop sweetShop = container.Resolve<SweetShop>();

            Assert.AreEqual(Jellybean.Lemon, sweetShop.DispenseJellyBean());
        }

        [Test]
        public void CanUseConstructedObject()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterInstance(new AnyJellybeanDispenser(Jellybean.Cocoa)).AsImplementedInterfaces().SingleInstance();
            builder.RegisterType<SweetVendingMachine>().AsSelf();
            builder.RegisterType<SweetShop>().AsSelf();

            IContainer container = builder.Build();
            SweetShop sweetShop = container.Resolve<SweetShop>();

            Assert.AreEqual(Jellybean.Cocoa, sweetShop.DispenseJellyBean());
        }

        [Test]
        public void CanUseObjectFactory()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.Register(c => new AnyJellybeanDispenser(Jellybean.Orange)).AsImplementedInterfaces();
            builder.RegisterType<SweetVendingMachine>().AsSelf();
            builder.RegisterType<SweetShop>().AsSelf();

            IContainer container = builder.Build();
            SweetShop sweetShop = container.Resolve<SweetShop>();

            Assert.AreEqual(Jellybean.Orange, sweetShop.DispenseJellyBean());
        }

        [Test]
        public void CanRegisterMultipleDispensers()
        {
            ContainerBuilder builder = new ContainerBuilder();
            builder.RegisterType<VanillaJellybeanDispenser>().AsImplementedInterfaces();
            builder.RegisterType<StrawberryJellybeanDispenser>().AsImplementedInterfaces();
            IContainer container = builder.Build();

            // the Resolve<IEnumerable<>> syntax is the only way in autofac
            IEnumerable<IJellybeanDispenser> dispensers = container.Resolve<IEnumerable<IJellybeanDispenser>>();

            Assert.IsNotNull(dispensers);
            Assert.AreEqual(2, dispensers.Count());
        }
    }
}

1 Comment

  • Brendan Forster said

    Some other things to note:

    "Like StructureMap, this emphasises that once the building is done, the container's data is immutable."

    Autofac V2 added support for updating an existing container (see http://code.google.com/p/autofac/wiki/NewInV2):

    var container = // something already built

    var updater = new ContainerBuilder();
    updater.RegisterType<A>();
    updater.Register(c => new B()).As<IB>();

    // Add the registrations to the container
    updater.Update(container);


    And there are extensions for readability:

    builder.RegisterType<VanillaJellybeanDispenser>()
    .As<IJellybeanDispenser>().SingleInstance();

    can be done as

    builder.RegisterType<VanillaJellybeanDispenser>()
    .AsImplementedInterfaces().SingleInstance();

    or

    builder.RegisterType<VanillaJellybeanDispenser>()
    .AsSelf().SingleInstance();

    if you want to get the concrete type from the container.

Add a Comment