Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call async method in Autofac registration?

I want to do call an awaitable async method during a registration like this:

// builder variable contains Autofac ContainerBuilder
builder.Register(
    (async (context, parameters) => // need async here
        {
            var someClass = new SomeClass(context.Resolve<ISomeDependency>());
            await someClass.SomeAsyncInitMethod(); // need to await result
            return someClass;
        })).As<ISomeClass>().SingleInstance();

SomeClass implements ISomeClass as Service. The important part is the someClass.SomeAsyncInitMethod() call. This is async, so because of this I need to await it here and put the async keyword into the Register method. But now Autofac thinks this returns a Task<SomeClass> which is not registerable as Service ISomeClass.

How to achieve the above and register SomeClass as ISomeClass when awaiting the async init Method?

like image 498
Beachwalker Avatar asked Jul 07 '16 10:07

Beachwalker


2 Answers

I think that doing any I/O intensive work at resolution phase is wrong design, because it's usually important to have full control over the order of this operations, catch their exceptions, repeat them, control time between some of them, etc.

Solution is to postpone them with factories. Let me replace SomeClass with a more meaningful NpgsqlConnection:

var builder = new ContainerBuilder();
builder.Register(context =>
{
    // make sure not to capture temporary context:
    // https://autofaccn.readthedocs.io/en/latest/advanced/concurrency.html#service-resolution
    var connectionString = context.Resolve<IConfiguration>().GetConnectionString("MyDb");

    return new Func<Task<NpgsqlConnection>>(async () =>
    {
        var connection = new NpgsqlConnection(connectionString);
        await connection.OpenAsync();
        return connection;
    });
});

And here's how connection user can look like:

public sealed class Repository
{
    private readonly Func<Task<NpgsqlConnection>> _connectionFactory;

    public Repository(Func<Task<NpgsqlConnection>> connectionFactory)
    {
        _connectionFactory = connectionFactory;
    }

    public async Task<string> GetServerVersionAsync()
    {
        using (var openedConnection = await _connectionFactory())
            return openedConnection.ServerVersion;
    }
}

Even if Autofac would support async registrations, it is still would be beneficial to require a factory in Repository constructor, because the connection is a limited resource and it is better to limit the time it is opened.

like image 152
astef Avatar answered Sep 22 '22 16:09

astef


This is an old question, but I think autofac does not support that.

We used:

builder.Register(c =>
  {
      var bar= c.Resolve<IBar>();
      var foo = new Foo(bar);
      return foo.ComputeAsync().ConfigureAwait(false).GetAwaiter().GetResult();
  })
.As<IFoo>()
.SingleInstance();

But as mentioned on the comments: Registering async factory in Autofac

like image 21
Vetras Avatar answered Sep 19 '22 16:09

Vetras