如何在Autofac注册中调用异步方法?

20

我想在注册过程中调用一个可等待异步方法,像这样:

// 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 实现了 ISomeClass 作为服务。 重要的部分是调用了 someClass.SomeAsyncInitMethod()。由于它是 async,所以我需要在这里使用 await 并将 async 关键字放入 Register 方法中。但现在 Autofac 认为这返回一个无法注册为 Service ISomeClassTask<SomeClass>

如何在等待 async 初始化方法时实现上述操作并将 SomeClass 注册为 ISomeClass

3
看起来类似于 https://dev59.com/BXDYa4cB1Zd3GeqPCJ3z - Damian Green
1
类似的问题/问题已提交:https://github.com/autofac/Autofac/issues/751 - Travis Illig
3个回答

9

我认为在解析阶段进行任何I/O密集型工作都是错误的设计,因为通常重要的是对这些操作的顺序、捕获它们的异常、重复它们、控制某些操作之间的时间等有完全的控制。

解决方案是用工厂推迟它们。让我用一个更有意义的NpgsqlConnection替换SomeClass

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;
    });
});

这是连接用户的样式示例:
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;
    }
}

即使Autofac支持异步注册,要求在Repository构造函数中需要一个工厂仍然是有益的,因为连接是一种有限的资源,最好限制它打开的时间。

6
这是一个老问题,我认为autofac不支持这种做法。
我们使用的是:
builder.Register(c =>
  {
      var bar= c.Resolve<IBar>();
      var foo = new Foo(bar);
      return foo.ComputeAsync().ConfigureAwait(false).GetAwaiter().GetResult();
  })
.As<IFoo>()
.SingleInstance();

但是如评论中所述:在Autofac中注册异步工厂


1
我们遇到了相同的问题,但找到了不同的解决方案。我想分享一下。您可以在Register方法中包装异步代码,并调用.GetResult().Wait()。这将生成一个新的上下文,确保任何异步代码在返回之前在该上下文中运行得很好。
其他响应片段中的示例:
builder.Register(c =>
  {
      var bar= c.Resolve<IBar>();
      var foo = new Foo(bar);
      var resultTask = Task.Run(async () => {
           return await foo.ComputeAsync();
      });
      return resultTask.GetResult();
  })
.As<IFoo>()
.SingleInstance();


这与 https://dev59.com/RloT5IYBdhLWcg3wtRYR#50451094 有何不同? - Vetras
这个对我有效,而 Vetras 的回答则无效。 - FreeAsInBeer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接