Autofac and Async Resources

I came across a problem on a recent WebAPI project where I wanted to use Autofac to inject some tenant information (i.e. derived per request) into the constructor of each controller:

public class MyController : ApiController
{
  public MyController(TenantInformation tenantInfo)
  {
  }
}

The problem was that the TenantInformation had to be sourced from an async API call

var tenantInfo = await tenantApi.GetTenantInfoAsync();

This means that you cannot implement something like the below to register the component

static void Main(string[] args)
{
  var builder = new ContainerBuilder();

  builder.Register(context => context.Resolve<TenantApi>().GetTenantInfo());

  var container = builder.Build();
  var example = container.Resolve<ExampleController>();
  // --> throws 'Autofac.Core.Registration.ComponentNotRegisteredException'
}

On closer examination of container we can see that TenantInfo has not been registered; instead we have registered an instance of Task<TenantInfo>.  We can await this but not from a constructor.
One option that I briefly considered was importing the service directly into each controller and then getting the value within each async action method that required it.  This works but it feels messy and against the point of DI.  I want to be able to depend on my dependencies; not on the providers of my dependencies.

Using a Mediator

My solution was to create a mediator object representing an asynchronously-resolved component:

interface IAsyncRegistration
{
  Task Resolve(IComponentContext context);
}

class AsyncRegistration<T> : IAsyncRegistration
{
  private Func<IComponentContext, Task<T>> _resolve;

  public AsyncRegistration(Func<IComponentContext, Task<T>> resolve)
  {
    _resolve = resolve;
  }

  public bool Resolved { get; private set; }

  public T Value { get; private set; }

  public async Task Resolve(IComponentContext context)
  {
    this.Value = await _resolve(context);
    this.Resolved = true;
  }
}

This class wraps an resolution function for the type, the resolved value and a flag to indicate whether or not it has been resolved. It also implements a non-generic interface so we can find all instances of AsyncRegistration<T> regardless of T.

public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle> RegisterAsync<T>(this ContainerBuilder builder, Func<IComponentContext, Task<T>> resolve)
{
  builder.RegisterInstance(new AsyncRegistration<T>(resolve))
    .AsSelf()
    .AsImplementedInterfaces();

  return builder.Register<T>(context =>
  {
    var asyncRegistration = context.Resolve<AsyncRegistration<T>>();
    if (!asyncRegistration.Resolved)
      throw new DependencyResolutionException($"Async component {typeof(T).Name} has not been resolved");

    return asyncRegistration.Value;
  });
}

Next I created an extension method for ContainerBuilder that adds 2 registrations:

  1. A registration of AsyncRegistration<T>
  2. A registration of <T> that resolves the AsyncRegistration<T>, checks that it has been resolved and then returns the result

Finally I created an extension method that can be called on the container from anywhere within an async block that will resolve all of the values

public static Task ResolveAsyncRegistrations(this IComponentContext context)
{
  var registrations = context.Resolve<IEnumerable<IAsyncRegistration>>();
  return Task.WhenAll(registrations.Select(r => r.Resolve(context)));
}

All together this means that the following will work and we can now inject asynchronously-resolved services into controller constructors:

var builder = new ContainerBuilder();
builder.RegisterAsync(context =&gt; context.Resolve&lt;TenantApi&gt;().GetTenantInfo());

var container = builder.Build();

//...in an async block...
await container.ResolveAsyncRegistrations();

//...then some time later...
var tenantInfo = container.Resolve<TenantInfo>();

Plugging in to WebAPI

The easiest way to plug this in to the WebAPI pipeline is to create a message handler that

  1. Gets an IComponentContext for the current request
  2. awaits a call to the ResolveAsyncRegistrations extension method
public class AsyncRegistrationHandler : DelegatingHandler
{
  protected override async Task&lt;HttpResponseMessage&gt; SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  {
    var scope = request.GetDependencyScope().GetRequestLifetimeScope();
    await scope.RegisterAsyncComponents();

    return await base.SendAsync(request, cancellationToken);
  }
}

Caveats

This system works for my particular scenario but there are a lot of possible situations where this would not work or would need extending.  The lifetime management of the dependencies, for example, is very rigid in this implementation and would need some work to be exposed properly.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s