Running Code at Startup

Autofac provides the ability for components to be notified or automatically activated when the container is built.

There are three automatic activation mechanisms available: - Startable components - Auto-activated components - Container build callbacks

In all cases, at the time the container is built, the component will be activated.

Startable Components

A startable component is one that is activated by the container when the container is initially built and has a specific method called to bootstrap an action on the component.

The key is to implement the Autofac.IStartable interface. When the container is built, the component will be activated and the IStartable.Start() method will be called.

This only happens once, for a single instance of each component, the first time the container is built. Resolving startable components by hand won’t result in their Start() method being called. It isn’t recommended that startable components implement other services, or be registered as anything other than SingleInstance().

Components that need to have something like a Start() method called each time they are activated should use a lifetime event like OnActivated instead.

To create a startable component, implement Autofac.IStartable:

public class StartupMessageWriter : IStartable
{
   public void Start()
   {
      Console.WriteLine("App is starting up!");
   }
}

Then register your component and be sure to specify it as IStartable or the action won’t be called:

var builder = new ContainerBuilder();
builder
   .RegisterType<StartupMessageWriter>()
   .As<IStartable>()
   .SingleInstance();

When the container is built, the type will be activated and the IStartable.Start() method will be called. In this example, a message will be written to the console.

Known issue: While the IStartable components will be resolved in dependency order, the Start() methods may not actually be called in dependency order. You can work around this using container build callbacks combined with SingleInstance registrations. See below.

Auto-Activated Components

An auto-activated component is a component that simply needs to be activated one time when the container is built. This is a “warm start” style of behavior where no method on the component is called and no interface needs to be implemented - a single instance of the component will be resolved with no reference to the instance held.

To register an auto-activated component, use the AutoActivate() registration extension.

var builder = new ContainerBuilder();
builder
   .RegisterType<TypeRequiringWarmStart>()
   .AsSelf()
   .AutoActivate();

Note: If you omit the AsSelf() or As<T>() service registration calls when you register an AutoActivate() component, the component will only be registered to auto-activate and won’t necessarily be resolvable “as itself” after container build.

Container Build Callbacks

You can register any arbitrary action to happen at container build time by registering a build callback. A build callback is an Action<IContainer> and will get the built container prior to that container being returned from ContainerBuilder.Build. Build callbacks execute in the order they are registered:

var builder = new ContainerBuilder();
builder
   .RegisterBuildCallback(c => c.Resolve<DbContext>());

// The callback will run after the container is built
// but before it's returned.
var container = builder.Build();

You can use build callbacks as another way to automatically start/warm up an object on container build. Do that by using them in conjunction with the lifetime event OnActivated and SingleInstance registrations. This can work around the known issue where IStartable implementations are resolved in dependency order but the Start() methods aren’t necessarily called in dependency order.

A long/contrived example in unit test form:

public class TestClass
{
  // Create a dependency chain like
  //    ==> 2 ==+
  // 4 =+       ==> 1
  //    ==> 3 ==+
  // 4 needs 2 and 3
  // 2 needs 1
  // 3 needs 1
  // Dependencies should start up in the order
  // 1, 2, 3, 4
  // or
  // 1, 3, 2, 4
  private class Dependency1
  {
    public Dependency1(ITestOutputHelper output)
    {
      output.WriteLine("Dependency1.ctor");
    }
  }

  private class Dependency2
  {
    private ITestOutputHelper output;

    public Dependency2(ITestOutputHelper output, Dependency1 dependency)
    {
      this.output = output;
      output.WriteLine("Dependency2.ctor");
    }

    public void Initialize()
    {
      this.output.WriteLine("Dependency2.Initialize");
    }
  }

  private class Dependency3
  {
    private ITestOutputHelper output;

    public Dependency3(ITestOutputHelper output, Dependency1 dependency)
    {
      this.output = output;
      output.WriteLine("Dependency3.ctor");
    }

    public void Initialize()
    {
      this.output.WriteLine("Dependency3.Initialize");
    }
  }

  private class Dependency4
  {
    private ITestOutputHelper output;

    public Dependency4(ITestOutputHelper output, Dependency2 dependency2, Dependency3 dependency3)
    {
      this.output = output;
      output.WriteLine("Dependency4.ctor");
    }

    public void Initialize()
    {
      this.output.WriteLine("Dependency4.Initialize");
    }
  }

  // Xunit passes this to the ctor of the test class
  // so we can capture console output.
  private ITestOutputHelper _output;

  public TestClass(ITestOutputHelper output)
  {
    this._output = output;
  }

  [Fact]
  public void OnActivatedDependencyChain()
  {
    var builder = new ContainerBuilder();
    builder.RegisterInstance(this._output).As<ITestOutputHelper>();
    builder.RegisterType<Dependency1>().SingleInstance();

    // The OnActivated replaces the need for IStartable. When an instance
    // is activated/created, it'll run the Initialize method as specified. Using
    // SingleInstance means that only happens once.
    builder.RegisterType<Dependency2>().SingleInstance().OnActivated(args => args.Instance.Initialize());
    builder.RegisterType<Dependency3>().SingleInstance().OnActivated(args => args.Instance.Initialize());
    builder.RegisterType<Dependency4>().SingleInstance().OnActivated(args => args.Instance.Initialize());

    // Notice these aren't in dependency order.
    builder.RegisterBuildCallback(c => c.Resolve<Dependency4>());
    builder.RegisterBuildCallback(c => c.Resolve<Dependency2>());
    builder.RegisterBuildCallback(c => c.Resolve<Dependency1>());
    builder.RegisterBuildCallback(c => c.Resolve<Dependency3>());

    // This will run the build callbacks.
    var container = builder.Build();

    // These effectively do NOTHING. OnActivated won't be called again
    // because they're SingleInstance.
    container.Resolve<Dependency1>();
    container.Resolve<Dependency2>();
    container.Resolve<Dependency3>();
    container.Resolve<Dependency4>();
  }
}

This sample unit test will generate this output:

Dependency1.ctor
Dependency2.ctor
Dependency3.ctor
Dependency4.ctor
Dependency2.Initialize
Dependency3.Initialize
Dependency4.Initialize

You’ll see from the output that the callbacks and OnActivated methods executed in dependency order. If you must have the activations and the startups all happen in dependency order (not just the activations/resolutions), this is the workaround.

Note if you don’t use SingleInstance then OnActivated will be called for every new instance of the dependency. Since “warm start” objects are usually singletons and are expensive to create, this is generally what you want anyway.