Access or get Autofac Container inside a static class

Jose Alonso Monge picture Jose Alonso Monge · Nov 27, 2015 · Viewed 20.5k times · Source

I need to get or access to my IoC container in a static class. This is my (simplified) scenario:

I register dependencies for ASP .net Web Api in a Startup class (but also I do this for MVC or WCF. I have a DependecyResolver project, but for simplicity, consider the following code)

// Web Api project - Startup.cs
public void Configuration(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();

    var builder = new ContainerBuilder();

    // ... Omited for clarity
    builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
        .AsClosedTypesOf(typeof(IHandle<>))
        .AsImplementedInterfaces();

    // ...
    IContainer container = builder.Build();
    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    // ...
}

Then, in a separate class library I have my static class (again simplified for clarity):

public static class DomainEvents
{
    private static IContainer Container { get; set; }

    static DomainEvents()
    {
        //Container = I need get my Autofac container here
    }

    public static void Register<T>(Action<T> callback) where T : IDomainEvent { /* ... */ }

    public static void ClearCallbacks() { /* ... */  }

    public static void Raise<T>(T args) where T : IDomainEvent
    {
        foreach (var handler in Container.Resolve<IEnumerable<IHandle<T>>>())
        {
            handler.Handle(args);
        }
        // ...
    }
}

Any idea how can I get this?

Answer

Steven picture Steven · Nov 27, 2015

I need to get or access to my IoC container in a static class. Any idea how can I get this?

Yes, you don't! Seriously. The pattern with the static DomainEvents class originates from Udi Dahan, but even Udi has admitted that this was a bad design. Static classes that require dependencies of their own are extremely painful to work with. They make the system hard to test and maintain.

Instead, create a IDomainEvents abstraction and inject an implementation of that abstraction into classes that require publishing events. This completely solves the your problem.

You can define your DomainEvents class as follows:

public interface IDomainEvents
{
    void Raise<T>(T args) where T : IDomainEvent;
}

// NOTE: DomainEvents depends on Autofac and should therefore be placed INSIDE
// your Composition Root.
private class AutofacDomainEvents : IDomainEvents
{
    private readonly IComponentContext context;
    public AutofacDomainEvents(IComponentContext context) {
        if (context == null) throw new ArgumentNullException("context");
        this.context = context;
    }

    public void Raise<T>(T args) where T : IDomainEvent {
        var handlers = this.context.Resolve<IEnumerable<IHandle<T>>>();
        foreach (var handler in handlers) {
            handler.Handle(args);
        }
    }
}

And you can register this class as follows:

IContainer container = null;

var builder = new ContainerBuilder();

builder.RegisterType<AutofacDomainEvents>().As<IDomainEvent>()
    .InstancePerLifetimeScope();

// Other registrations here

container = builder.Build();