Registration Concepts

You register components with Autofac by creating a ContainerBuilder and informing the builder which components expose which services.

Components can be created via reflection (by registering a specific .NET type or open generic); by providing a ready-made instance (an instance of an object you created); or via lambda expression (an anonymous function that executes to instantiate your object). ContainerBuilder has a family of Register() methods that allow you to set these up.

Each component exposes one or more services that are wired up using the As() methods on ContainerBuilder.

// Create the builder with which components/services are registered.
var builder = new ContainerBuilder();

// Register types that expose interfaces...
builder.RegisterType<ConsoleLogger>().As<ILogger>();

// Register instances of objects you create...
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

// Register expressions that execute to create objects...
builder.Register(c => new ConfigReader("mysection")).As<IConfigReader>();

// Build the container to finalize registrations
// and prepare for object resolution.
var container = builder.Build();

// Now you can resolve services using Autofac. For example,
// this line will execute the lambda expression registered
// to the IConfigReader service.
using(var scope = container.BeginLifetimeScope())
{
  var reader = scope.Resolve<IConfigReader>();
}

Reflection Components

Register by Type

Components generated by reflection are typically registered by type:

var builder = new ContainerBuilder();
builder.RegisterType<ConsoleLogger>();
builder.RegisterType(typeof(ConfigReader));

When using reflection-based components, Autofac automatically uses the constructor for your class with the most parameters that are able to be obtained from the container.

For example, say you have a class with three constructors like this:

public class MyComponent
{
    public MyComponent() { /* ... */ }
    public MyComponent(ILogger logger) { /* ... */ }
    public MyComponent(ILogger logger, IConfigReader reader) { /* ... */ }
}

Now say you register components and services in your container like this:

var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
var container = builder.Build();

using(var scope = container.BeginLifetimeScope())
{
  var component = scope.Resolve<MyComponent>();
}

When you resolve your component, Autofac will see that you have an ILogger registered, but you don’t have an IConfigReader registered. In that case, the second constructor will be chosen since that’s the one with the most parameters that can be found in the container.

An important note on reflection-based components: Any component type you register via RegisterType must be a concrete type. While components can expose abstract classes or interfaces as services, you can’t register an abstract/interface component. It makes sense if you think about it: behind the scenes, Autofac is creating an instance of the thing you’re registering. You can’t “new up” an abstract class or an interface. You have to have an implementation, right?

Specifying a Constructor

You can manually choose a particular constructor to use and override the automatic choice by registering your component with the UsingConstructor method and a list of types representing the parameter types in the constructor:

builder.RegisterType<MyComponent>()
       .UsingConstructor(typeof(ILogger), typeof(IConfigReader));

Note that you will still need to have the requisite parameters available at resolution time or there will be an error when you try to resolve the object. You can pass parameters at registration time or you can pass them at resolve time.

Note

You can find advanced methods of customising which constructor to use here.

Required Properties

Starting in Autofac 7.0, in a reflection-based component, all required properties are automatically resolved, in the same manner as constructor parameters.

All required properties of the component must be resolvable services (or supplied as a parameter) otherwise an exception will be thrown when trying to resolve the component.

For example, consider a class with these properties:

public class MyComponent
{
  public required ILogger Logger { protected get; init; }

  public required IConfigReader ConfigReader { protected get; init; }
}

You can register and use this class as you could if it had a constructor:

var builder = new ContainerBuilder();
builder.RegisterType<MyComponent>();
builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<ConfigReader>().As<IConfigReader>();
var container = builder.Build();

using(var scope = container.BeginLifetimeScope())
{
  // Logger and ConfigReader will be populated.
  var component = scope.Resolve<MyComponent>();
}

Required properties are also set automatically on all base classes (if they are present); this makes required properties useful with deep object hierarchies, because it allows you to avoid having to invoke base constructors with the set of services; Autofac will set the base class properties for you.

Note

For more details on required property injection, see the dedicated section in the property injection documentation.

Instance Components

In some cases, you may want to pre-generate an instance of an object and add it to the container for use by registered components. You can do this using the RegisterInstance method:

var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();

Something to consider when you do this is that Autofac automatically handles disposal of registered components and you may want to control the lifetime yourself rather than having Autofac call Dispose on your object for you. In that case, you need to register the instance with the ExternallyOwned method:

var output = new StringWriter();
builder.RegisterInstance(output)
       .As<TextWriter>()
       .ExternallyOwned();

Registering provided instances is also handy when integrating Autofac into an existing application where a singleton instance already exists and needs to be used by components in the container. Rather than tying those components directly to the singleton, it can be registered with the container as an instance:

builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();

This ensures that the static singleton can eventually be eliminated and replaced with a container-managed one.

The default service exposed by an instance is the concrete type of the instance. See “Services vs. Components,” below.

Lambda Expression Components

Reflection is a pretty good default choice for component creation. Things get messy, though, when component creation logic goes beyond a simple constructor call.

Autofac can accept a delegate or lambda expression to be used as a component creator:

builder.Register(c => new A(c.Resolve<B>()));

The parameter c provided to the expression is the component context (an IComponentContext object) in which the component is being created. You can use this to resolve other values from the container to assist in creating your component. It is important to use this rather than a closure to access the container so that deterministic disposal and nested containers can be supported correctly.

Additional dependencies can be satisfied using this context parameter - in the example, A requires a constructor parameter of type B that may have additional dependencies.

As well as using IComponentContext to resolve dependencies in your lambda expression, you can also use the generic Register overloads to specify your dependencies as a variable number of typed arguments to the lambda, and Autofac will resolve them for you:

builder.Register((IDependency1 dep1, IDependency2 dep2) => new Component(dep1, dep2));

You can blend the IComponentContext and generic approach if you need to make conditional choices, or use methods like ResolveNamed. Just add the IComponentContext as the first parameter to the lambda:

builder.Register((IComponentContext ctxt, IDependency1 dep1) => new Component(dep1, ctxt.ResolveNamed<IDependency2>("value")));

The default service provided by an expression-created component is the inferred return type of the expression.

Below are some examples of requirements met poorly by reflective component creation but nicely addressed by lambda expressions.

Complex Parameters

Constructor parameters can’t always be declared with simple constant values. Rather than puzzling over how to construct a value of a certain type using an XML configuration syntax, use code:

builder.Register(c => new UserSession(DateTime.Now.AddMinutes(25)));

(Of course, session expiry is probably something you’d want to specify in a configuration file - but you get the gist ;))

Property Injection

While Autofac offers a more first-class approach to property injection, you can use expressions and property initializers to populate properties as well:

builder.Register(c => new A(){ MyB = c.ResolveOptional<B>() });

The ResolveOptional method will try to resolve the value but won’t throw an exception if the service isn’t registered. (You will still get an exception if the service is registered but can’t properly be resolved.) This is one of the options for resolving a service.

Property injection is not recommended in the majority of cases. Alternatives like the Null Object pattern, overloaded constructors or constructor parameter default values make it possible to create cleaner, “immutable” components with optional dependencies using constructor injection.

Selection of an Implementation by Parameter Value

One of the great benefits of isolating component creation is that the concrete type can be varied. This is often done at runtime, not just configuration time:

builder.Register<CreditCard>(
  (c, p) =>
    {
      var accountId = p.Named<string>("accountId");
      if (accountId.StartsWith("9"))
      {
        return new GoldCard(accountId);
      }
      else
      {
        return new StandardCard(accountId);
      }
    });

In this example, CreditCard is implemented by two classes, GoldCard and StandardCard - which class is instantiated depends on the account ID provided at runtime.

Parameters are provided to the creation function through an optional second parameter named p in this example.

Using this registration would look like:

var card = container.Resolve<CreditCard>(new NamedParameter("accountId", "12345"));

You can use parameters with the Func<X, Y> relationship by using typed instead of named parameters.

builder.Register<CreditCard>(
  (c, p) =>
    {
      // Typed instead of named to work with Func<X, Y>
      var accountId = p.TypedAs<string>();
      if (accountId.StartsWith("9"))
      {
        return new GoldCard(accountId);
      }
      else
      {
        return new StandardCard(accountId);
      }
    });

Using this registration would look like:

var cardFactory = container.Resolve<Func<string, CreditCard>>();
var card = cardFactory("12345");

You can use lambda expression components here, too, to make it even simpler:

builder.Register<string, CreditCard>(
  (accountId) =>
    {
      if (accountId.StartsWith("9"))
      {
        return new GoldCard(accountId);
      }
      else
      {
        return new StandardCard(accountId);
      }
    });

Another clean, type-safe syntax can be achieved if a delegate to create CreditCard instances is declared and a delegate factory is used. Delegate factories are the way you can support more than one parameter of the same type.

Open Generic Components

Autofac supports open generic types. Use the RegisterGeneric() builder method:

builder.RegisterGeneric(typeof(NHibernateRepository<>))
       .As(typeof(IRepository<>))
       .InstancePerLifetimeScope();

When a matching service type is requested from the container, Autofac will map this to an equivalent closed version of the implementation type:

// Autofac will return an NHibernateRepository<Task>
var tasks = container.Resolve<IRepository<Task>>();

Registration of a specialized service type (e.g. IRepository<Person>) will override the open generic version.

You can also use delegates to provide the closed generic type, if you have some custom behavior for choosing the closed generic implementation:

var builder = new ContainerBuilder();

builder.RegisterGeneric((context, types, parameters) =>
{
    // Make decisions about what closed type to use.
    if (types.Contains(typeof(string)))
    {
        return new StringSpecializedImplementation();
    }

    return Activator.CreateInstance(typeof(GeneralImplementation<>).MakeGenericType(types));
}).As(typeof(IService<>));

Note

Be aware that the delegate form of RegisterGeneric will usually be slightly less performant than the reflection-based version, because the closed generic type cannot be cached in the same way.

Services vs. Components

When you register components, you have to tell Autofac which services that component exposes. By default, most registrations will just expose themselves as the type registered:

// This exposes the service "CallLogger"
builder.RegisterType<CallLogger>();

Components can only be resolved by the services they expose. In this simple example it means:

// This will work because the component
// exposes the type by default:
scope.Resolve<CallLogger>();

// This will NOT work because we didn't
// tell the registration to also expose
// the ILogger interface on CallLogger:
scope.Resolve<ILogger>();

You can expose a component with any number of services you like:

builder.RegisterType<CallLogger>()
       .As<ILogger>()
       .As<ICallInterceptor>();

Once you expose a service, you can resolve the component based on that service. Note, however, that once you expose a component as a specific service, the default service (the component type) is overridden:

// These will both work because we exposed
// the appropriate services in the registration:
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();

// This WON'T WORK anymore because we specified
// service overrides on the component:
scope.Resolve<CallLogger>();

If you want to expose a component as a set of services as well as using the default service, use the AsSelf method:

builder.RegisterType<CallLogger>()
       .AsSelf()
       .As<ILogger>()
       .As<ICallInterceptor>();

Now all of these will work:

// These will all work because we exposed
// the appropriate services in the registration:
scope.Resolve<ILogger>();
scope.Resolve<ICallInterceptor>();
scope.Resolve<CallLogger>();

Default Registrations

If more than one component exposes the same service, Autofac will use the last registered component as the default provider of that service:

builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<FileLogger>().As<ILogger>();

In this scenario, FileLogger will be the default for ILogger because it was the last one registered.

To override this behavior, use the PreserveExistingDefaults() modifier:

builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<FileLogger>().As<ILogger>().PreserveExistingDefaults();

In this scenario, ConsoleLogger will be the default for ILogger because the later registration for FileLogger used PreserveExistingDefaults().

Conditional Registration

Note

Conditional registration was introduced in Autofac 4.4.0

In most cases, overriding registrations as noted in the section above, “Default Registrations,” is enough to get the right component resolved at runtime. Ensuring things get registered in the right order; using PreserveExistingDefaults(); and taking advantage of lambda/delegate registrations for more complex conditions and behavior can get you pretty far.

There can be a few scenarios where this may not be the way you want to go:

  • You don’t want the component present in the system if something else is handling the functionality. For example, if you resolve an IEnumerable<T> of a service, all of the registered components implementing that service will be returned, whether or not you’ve used PreserveExistingDefaults(). Usually this is fine, but there are some edge cases where you may not want that.

  • You only want to register the component if some other component isn’t registered; or only if some other component is registered. You can’t resolve things out of a container that you’re building, and you shouldn’t update a container that’s already built. Being able to conditionally register a component based on other registrations can be helpful.

There are two registration extensions that can help in these cases:

  • OnlyIf() - Provide a lambda that uses an IComponentRegistryBuilder to determine if a registration should happen.

  • IfNotRegistered() - Shortcut to stop a registration from happening if some other service is already registered.

These extensions run at the time of ContainerBuilder.Build() and will execute in the order of the actual component registrations. Here are some examples showing how they work:

var builder = new ContainerBuilder();

// Only ServiceA will be registered.
// Note the IfNotRegistered takes the SERVICE TYPE to
// check for (the As<T>), NOT the COMPONENT TYPE
// (the RegisterType<T>).
builder.RegisterType<ServiceA>()
       .As<IService>();
builder.RegisterType<ServiceB>()
       .As<IService>()
       .IfNotRegistered(typeof(IService));

// HandlerA WILL be registered - it's running
// BEFORE HandlerB has a chance to be registered
// so the IfNotRegistered check won't find it.
//
// HandlerC will NOT be registered because it
// runs AFTER HandlerB. Note it can check for
// the type "HandlerB" because HandlerB registered
// AsSelf() not just As<IHandler>(). Again,
// IfNotRegistered can only check for "As"
// types.
builder.RegisterType<HandlerA>()
       .AsSelf()
       .As<IHandler>()
       .IfNotRegistered(typeof(HandlerB));
builder.RegisterType<HandlerB>()
       .AsSelf()
       .As<IHandler>();
builder.RegisterType<HandlerC>()
       .AsSelf()
       .As<IHandler>()
       .IfNotRegistered(typeof(HandlerB));

// Manager will be registered because both an IService
// and HandlerB are registered. The OnlyIf predicate
// can allow a lot more flexibility.
builder.RegisterType<Manager>()
       .As<IManager>()
       .OnlyIf(reg =>
         reg.IsRegistered(new TypedService(typeof(IService))) &&
         reg.IsRegistered(new TypedService(typeof(HandlerB))));

// Be careful with open generics - IfNotRegistered and IsRegistered
// only work with CLOSED generics because that's what you'd resolve!
// Note the check is for a closed generic that would be registered
// as part of the open generic - if you put an open generic in the
// IfNotRegistered check, it'll always appear not registered.
builder.RegisterGeneric(typeof(CommandHandler<>))
       .As(typeof(ICommandHandler<>))
       .IfNotRegistered(typeof(ICommandHandler<MyCommand>));

// This is when the conditionals actually run. Again,
// they run in the order the registrations were added
// to the ContainerBuilder.
var container = builder.Build();

Configuration of Registrations

You can use XML or programmatic configuration (“modules”) to provide groups of registrations together or change registrations at runtime. You can also use use Autofac modules for some dynamic registration generation or conditional registration logic.

Dynamically-Provided Registrations

Autofac modules are the simplest way to introduce dynamic registration logic or simple cross-cutting features. For example, you can use a module to dynamically attach a log4net logger instance to a service being resolved.

If you find that you need even more dynamic behavior, such as adding support for a new implicit relationship type, you might want to check out the registration sources section in the advanced concepts area.