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?
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With