Skip to content

Instantly share code, notes, and snippets.

@nthdeveloper
Last active June 12, 2019 21:34
Show Gist options
  • Save nthdeveloper/6ef35120af6b079e99d7076b5d3ac317 to your computer and use it in GitHub Desktop.
Save nthdeveloper/6ef35120af6b079e99d7076b5d3ac317 to your computer and use it in GitHub Desktop.
Autofac cheeat sheet for the most common scenarios.

Documentation

Online

PDF

Simplest Usage

ContainerBuilder builder = new ContainerBuilder();
//... register components
var container = builder.Build();
var service = container.Resolve<IService>();

Simple Registerations

//Register an instance
builder.RegisterInstance(new ConsoleLogger()).As<ILogger>();
//Register concrete type
builder.RegisterType<Car>();
//Register by implemented services
builder.RegisterType<ConsoleLogger>()
   .As<ILogger>();//Register as ILog
   .AsSelf();//also register ConsoleLogger type, so ConsoleLogger becomes resolvable

//Register as multiple services
builder.RegisterType<EmailLogger>().As<ILogger>().As<ISomeInterface>();
//Last registered type for a service is used when the service is resolved but this behavior can be changed
builder.RegisterType<EmailLogger>().As<ILogger>();
builder.RegisterType<ConsoleLogger>().As<ILogger>().PreserveExistingDefaults();//Does not overwrite EmailLogger, still EmailLogger will be used
//Register as generic type
builder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>));
var myList = container.Resolve<IList<int>>();//Returns new List<int>()

Register a Lambda Expression for a Component

  • Register a lambda for a component registeration, (type of c is IComponentContext)
  • This lambda does not run immediately, it is executed when resolving
  • Runs whenever this dependency is needed (based on the instance mode, default is InstancePerDependency
builder.Register(c => new Engine(c.Resolve<ILogger>(), 123));

Selecting Constructors

  • Constructor with most arguments will be used by default
  • Explicitly specify which ctor to use
  builder.RegisterType<Car>().UsingConstructor(typeof(Engine));
  builder.RegisterType<Car>().UsingConstructor(typeof(Engine), typeof(ILogger));

Resolving Constructor Parameters (different ways)

//Named parameter
builder.RegisterType<SMSLogger>().As<ILogger>().WithParameter("phoneNumber", "+12345");//or
builder.RegisterType<SMSLogger>().As<ILogger>().WithParameter(new NamedParameter("phoneNumber", "+12345"));
//Typed parameter
builder.RegisterType<SMSLogger>().As<ILogger>().WithParameter(new TypedParameter(typeof(string), "+12345"));
//Resolved parameter (requires using Autofac.Core;)
builder.RegisterType<SMSLogger>().As<ILogger>()
		.WithParameter(
		new ResolvedParameter //(predicate, value provider)
		(
			//pi=ParameterInfo, ctx=IComponentContext
			(pi, ctx)=> pi.ParameterType == typeof(string) && pi.Name == "phoneNumber",
			(pi, ctx)=> "+12345"
		));
//Resolving at activation time
builder.RegisterType<Child>().WithParameter((p, c) => p.Name == "Parent", (p, c) => c.Resolve<Parent>());
//Multiple paramters
builder.RegisterType<SMSLogger>().As<ILogger>().WithParameters(new Parameter[]{ ... });

Passing Parameters to Resolve

//(c=IComponentContext, p=IEnumerable<Patrameter>)
//1) Provide named parameter while registering and retrieve it in registered lambda
builder.RegisterType<SMSLogger>().As<ILogger>().WithParameter("phoneNumber", "+12345");
var log = container.Resolve<ILogger>();

//2) Use named parameter provided in resolve call
builder.Register((c, p) => new SMSLogger(p.Named<string>("phoneNumber")));//Retrieve named parameter while constructing the dependency
var log = container.Resolve<ILogger>(new NamedParameter("phoneNumber", "+12345"));//Resolve with named parameter

//3) Provide the named parameter while resolving the component
builder.RegisterType<SMSLogger>().As<ILogger>();
var log = container.Resolve<ILogger>(new NamedParameter("phoneNumber", "+12345"));//Resolve with named parameter

Property Injection

builder.RegisterType<Child>().PropertiesAutowired();

Method Injection

//1 Register lambda expression
builder.Register(c =>
            {
                var child = new Child();
                child.SetParent(c.Resolve<Parent>());
                return child;
            }).As<Child>();
			
//2 Use OnActivated event
builder.RegisterType<Child>().OnActivated((IActivatedEventArgs<Child> e) =>
           {
               var p = e.Context.Resolve<Parent>();
               e.Instance.SetParent(p);//Inject to method
           });

Scanning for Types

//1 Simple
builder.RegisterAssemblyTypes(assembly);
  
//2 With filter and additionla customization
builder.RegisterAssemblyTypes(assembly)
                .Where(t => t.Name.EndsWith("Log"))
                .Except<SMSLogger>()
                .Except<ConsoleLogger>(c => c.As<ILogger>().SingleInstance())
                .AsSelf();
				
//3 Register by reflection
builder.RegisterAssemblyTypes(assembly)
                .Except<SMSLogger>()
                .Where(t => t.Name.EndsWith("Log"))
                .As((Type t) => t.GetInterfaces()[0]);//Register the first interface the types implements

Scanning for Modules

//Register all modules in an assembly
builder.RegisterAssemblyModules(Assembly.GetExecutingAssembly())//params Assembly[]
//Register a single module or all modules derived from the given base module type
builder.RegisterAssemblyModules<ParentChildModule>(Assembly.GetExecutingAssembly());//params Assembly[]

Implicit Relationship Types

  • Delayed instantiation Replace the constructor parameter with Lazy<T> e.g. Lazy<Parent> parent

  • Controlled instantiation

    • Used to control object lifetime by the owner (dependent) object, e.g call Dispose by the owner. Normally Autofac calls Dispose
    • Inject Owned, Use myField.Value, call myField.Dispose() (from Owned type not .Value)
    • Deriving a class from Owned like MyOwned : Owned and using it will not work
  • Dynamic instantiation

    • Inject auto-generated factory. Inject and store component as Func. Call myField() to create the component
    • No need to register Func function to Autofac, autofac creates it automatically like Lazy
  • Parametrized instantiation

    • Also makes an auto-generated factory. Allows us to provide parameters. Inject Func<TArg1, TArg2, T> e.g Func<string, Child>
    • Call myField("John") to construct dependency
    • Does not support duplicate types in parameters. To resolve with dup types use a custom delegate
  • Enumeration

    • Inject IEnumerable, IList or ICollection
    • If no ILog is registered
      • container.Resolve()//throws exception
      • container.Resolve<IList>()//returns an empty list
  • Metadata Interrogation

    • Allows to attach metadata to a component. Inject and store Meta -> Autofac type
    • RegisterType().WithMetadata("name", "value");
    • Use myField.Metadata["foo"] to read metadata
    • Use myField.Value to get the object
    • It is possible to read metadata with dynamic object way like myField.Metadata.LogMode
      • For this, use another overload of metadata registeration "RegisterType().WithMetadata(c=>c.For(x=> x.LogMode, "verbose"))"
      • Inject System.ComponentModel.Composition.Lazy<T, TMedata>
      • Use myField.Value to get the dependency
      • Use myField.Metadata to access to metadata object
  • Keyed Service Lookup

    • Lets you pick a service based on a service key
    • Inject and store an IIndex<TKey, T>, TKey-> Key type e.g. string, T is the object type e.g. ILog
    • Register, builder.RegisterType().Keyed("console");
    • Access in dependent object myField["console"]

Scope and Lifetime

Instance Lifetime

builder.RegisterType<ConsoleLogger>().As<ILogger>()
               .SingleInstance()//Singleton
               .InstancePerDependency()//Create new for each dependency
               .InstancePerRequest()//Share one instance for the same context in web/HTTP/API request (only available with MVC, Web API, WebForms... etc)
               .InstancePerMatchingLifetimeScope("tag")//share the same instance for the scope tagged with "tag"
               .InstancePerLifetimeScope();//Single instance per lifetime scope

Lifetime Scope

//Life of the created objects end after the using scope (they are cleared and Dispose called)
using (var scope = container.BeginLifetimeScope())
{
    var logger = scope.Resolve<ILogger>();
    logger.Log("sdsdsd");
}

Instance Per Lifetime Scope

//Following code creates 2 ConsoleLoggers. Event if the scope2 is nested inside scope1 with a call scope1.BeginLife...() still creates 2 ConsoleLoggers
using (var scope1 = container.BeginLifetimeScope())
{
    var logger = scope1.Resolve<ILogger>();
}

using (var scope2 = container.BeginLifetimeScope())
{
    var logger = scope2.Resolve<ILogger>();
}

Instance Per Matching Lifetime Scope

  • .InstancePerMatchingLifetimeScope("tag")//share the same instance for the scope tagged with "tag"
    • Following code creates only one (1) ConsoleLogger
    • After creating a named scope all other calls to container.BeginLifetimeScope() throws exception, even outside the using block
using (var scope1 = container.BeginLifetimeScope("tag"))
{
    for (int i = 0; i < 3; i++)
    {
        var logger1 = scope1.Resolve<ILogger>();
    }

    using (var scope2 = scope1.BeginLifetimeScope())
    {
        for (int i = 0; i < 3; i++)
        {
            var logger2 = scope2.Resolve<ILogger>();
        }
    }
}

Instance Per Request

  • .InstancePerRequest()
    • Used by previous ASP.Net MVC 5 and older
    • ASP.NET Core uses Instance Per Lifetime Scope rather than Instance Per Request.

Captive Dependencies

  • An "instance per dependency" object lives as long as its owner lives and destroyed when its owner is destoryed (Disposed and released)

Disposal

Autofac controls the life time of the created dependencies and automatically Dispose them when they are no longer needed
builder.RegisterType<ConsoleLog>();//Autofac automatically disposes when it is no longer needed
builder.RegisterType<ConsoleLog>().ExternallyOwned();//Autofac does not dispose those instances
builder.RegisterInstance(new ConsoleLog());//Makes the instane Singleton. It is not registered after using of BeginLifetimeScope. But it is dispsed if container is disposed (using( var container = bulder.Build()...)) )

Lifetime Events

OnActivating

//a: IActivatingEventArgs<ILog>
builder.RegisterType<Child>()
          .OnActivating(a =>
          {              
              a.Instance.Parent = a.Context.Resolve<Parent>();//Can crteate/replace dependencies (this is not a constructor dependency it is just a property)
              a.ReplaceInstance(new BadChild());//Can replace the created instance with another instance
          });
//This  does not work, causes exception, next approach must be used
builder.RegisterType<ConsoleLog>().As<ILog>().OnActivating(a => { a.ReplaceInstance(new SMSLog("+123456")); });
//This two step configuration works
builder.RegisterType<ConsoleLog>().AsSelf();
builder.Register<ILog>(c => c.Resolve<ConsoleLog>()).OnActivating(a => a.ReplaceInstance(new SMSLog("+123456")));

OnActivated

//a: IActivatedEventArgs<ILog>
builder.Register<ILog>(c => c.Resolve<ConsoleLog>()).OnActivated(a => { Console.WriteLine("Component activated"); });

OnRelease

//Run a supplied action instead of disposing instances when they're no longer required.
//a: ILog
builder.Register<ILog>(c => c.Resolve<ConsoleLog>()).OnRelease(a => { Console.WriteLine("Component about to be released"); });

Running Code at Startup

  • Implement IStartable interface and register that interface for the component also.
  • Autofac automatically calls Start() after creating the instance
class MyClass : IStartable { public void Start() {...} }//This interface is from Autofac...
//Register as IStartable also
builder.RegisterType<MyClass>().AsSelf().As<IStartable>().SingleInstance();

Configuration via Modules

Derive from Autofac.Module and override "void Load(ContainerBuilder builder)" method withoud base.Load() call Modules are registered to the builder

builder.RegisterModule(new TransportModule { ObeySpeedLimit = true });

Module can have properties or get ctor parameters and change registration logic (decide what to register/strategy)

if (ObeySpeedLimit)
     builder.RegisterType<SaneDriver>().As<IDriver>();

Registration Sources

  • Use builder.RegisterSource(..) to register different types of sources
  • To register all the concrete types (enable concrete type injection without explicit registration)
b.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
  • Implement IRegistrationSource interface and register it as a source in order to do custom type resolving and object construction

Adapters

One button type and many ICommand types registered. To get one button for each registered ICommand type in Editor constructor

  • Register ICommand with its adapter (simple)
//*** Button should not be reigstered separately, otherwise one more Button is created for the last registered ICommand
//Button(ICommand cmd), Editor(IEnumerable<Button> buttons)
builer.RegisterAdapter(ICommand, Button>(cmd => new Button(cmd));
  • If metadata is added while registering the ICommand types (Meta<> requires "using Autofac.Features.Metadata;")
builder.RegisterType<SaveCommand>().As<ICommand>().WithMetadata("Name", "Save");
builder.RegisterAdapter<Meta<ICommand>, Button>(cmdMeta => new Button(cmdMeta.Value, (string)cmdMeta.Metadata["Name"]));

Decorators

ReportingService : IReportingService, ReportingServiceWithLogging : IReportingService, ReportingServiceWithLogging(IReportingService decorated)

//Register ordinary service 
builder.RegisterType<ReportingService>().Named<IReportingService>("reporting");
//Register as decorator 
builder.RegisterDecorator<IReportingService>((context, service) => new ReportingServiceWithLogging(service), "reporting");
//Resolving service returns the decorator type "ReportingServiceWithLogging" 
c.Resolve<IReportingService>()

Circular Dependencies

  • If both of them gets dependencies via properties register them as
   b.RegisterType<ParentWithProperty>().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies); 
   b.RegisterType<ChildWithProperty>().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
  • If one of them gets the other via constructor, register parent normally and child with property injection
  • Parent gets child in ctor ParentWithConstructor(ChildWithProperty child)

Aggregate Services

  • Instead of injecting many interfaces, inject single interface whose properties are those services (only get accessor is enough)
  • Autofac.Extras.AggregateService Nuget package must be added and the namespace
  • Register individual services with interfaces
  • Register aggregate service interface (no concrete class implementation is needed, it is generated by Autofac)
builder.RegisterAggregateService<IMyAggregateService>();
  • If Service4 : IService4 has a ctor parameter and the aggregate interface has a method which takes this argument and returns IService4 also works
calss Service4 : IService4
{
  publicService4(string name){}
}

interface IMyAggregateService
{
   //Parameter "name" is automatically passed to the constructor of Service4
   IService4 GetFourthService(string name);
}

Type Interceptor (Aspect Oriented Programming)

  • Add nuget package and namespace "Autofac.Extras.DynamicProxy"
  • Create a class that implements IInterceptor, implement "void Intercept(IInvocation invocation)"
  • Use (pre-invocation): invocation.Method.Name, invocation.Arguments
  • Call (invocation): invocation.Proceed();
  • Get return value (post-invocation): invocation.ReturnValue
  • Register as interceptor
builder.Register(c => new CallLogger(Console.Out)).As<IInterceptor>().AsSelf();
  • Enable interception for a type
builder.RegisterType<Audit>().EnableClassInterceptors();

MVC 5 Integration

  • Add Autofac and Autofac.MVC5 nuget packages
  • Add following using statements
using Autofac;
using Autofac.Integration.Mvc;
using AutofacAspNetMvcDemos.Controllers;
using RegistrationExtensions = Autofac.Builder.RegistrationExtensions;
  • in Global.asax::Application_Start()
  • Prepare builder and container
var builder = new ContainerBuilder();
builder.RegisterType<TextFileLogger>().As<ILogger>();
builder.RegisterControllers(GetType().Assembly);  

//or register controllers one by one
builder.RegisterType<HomeController>().InstancePerRequest();

// Register model binders
builder.RegisterModelBinders(GetType().Assembly);
builder.RegisterModelBinderProvider();

//Auto register abstractions "Instance per Request" lifetime
builder.RegisterModule<AutofacWebTypesModule>();
//Add property injection for View classes (custom abstract view base classes actaully)
builder.RegisterSource(new ViewRegistrationSource());
//Register dependency injection support for filter provider (properties are injected)
builder.RegisterFilterProvider();
//Create container and set dependency resolver for MVC5
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

Best Practices

  • Use As() in delegate registrations
builder.Register(c => new Component()).As<IComponent>();
  • Use constructor injection
  • Register frequently-used components with lambdas
builder.RegisterType<Component>();//slower
builder.Register(c => new Component());//faster
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment