C# 在Autofac中注册异步工厂

C# 在Autofac中注册异步工厂,c#,autofac,async-await,c#-5.0,C#,Autofac,Async Await,C# 5.0,我有一个钱包类,可以从存储库中获取。我正在尝试在Autofac中正确注册这两个类,以便使用钱包的类可以有一个正确的实例。问题是存储库使用异步方法(返回任务)。Autofac是否支持此类情况 这不起作用: 调用container.Resolve()时,我会收到一个异常消息,称钱包未注册。除非我弄错了,否则Autofac不支持异步工厂。您仍然可以让它工作,但您必须编写一些样板代码,因为构造函数注入无法工作。你还必须更直截了当地说你想要Task,而不是T。整个代码如下所示: cb.RegisterT

我有一个钱包类,可以从存储库中获取。我正在尝试在Autofac中正确注册这两个类,以便使用钱包的类可以有一个正确的实例。问题是存储库使用异步方法(返回任务)。Autofac是否支持此类情况

这不起作用:


调用
container.Resolve()
时,我会收到一个异常消息,称钱包未注册。

除非我弄错了,否则Autofac不支持
异步工厂。您仍然可以让它工作,但您必须编写一些样板代码,因为构造函数注入无法工作。你还必须更直截了当地说你想要
Task
,而不是
T
。整个代码如下所示:

cb.RegisterType<WalletRepository>()
  .As<IWalletRepository>()
  .SingleInstance();
cb.Register(c => c.Resolve<IWalletRepository>().CreateAsync(App.WalletPath));
cb.Register(async c => new ViewModel(await c.Resolve<Task<Wallet>>()))
  .SingleInstance();
var container = cb.Build();
var viewModel = await container.Resolve<Task<ViewModel>>();
cb.RegisterType()
.As()
.SingleInstance();
寄存器(c=>c.Resolve().CreateAsync(App.WalletPath));
cb.Register(异步c=>newviewmodel(等待c.Resolve())
.SingleInstance();
var container=cb.Build();
var viewModel=await container.Resolve();

Autofac可能有一些扩展点来简化代码,但我对它的了解还不够,无法帮助您实现这一点。

一个选项就是让注册完成将异步调用转换为结果所需的阻塞

即:

cb.Register(c=>c.Resolve().CreateAsync(App.WalletPath.Result);//注意:阻塞呼叫
这意味着在解决依赖关系时,AutoFac将被迫执行阻塞,并且服务的客户端不需要知道服务工厂是异步的


当然,这是否是一个好主意很大程度上取决于您当时正在做什么。

这可能会导致死锁。Autofac在处理不同生存期范围解析的某些逻辑中使用锁。因此,如果异步工厂操作试图在另一个线程中解决某个问题,则会出现死锁

您是否可以包括使用代码,或者至少包括使用注入依赖项的ctor和示例方法?没有回答这个问题,但我想说,您的WalletRepository首先不需要初始化IO。尝试在第一次使用它的一个方法时惰性地初始化它。它不是一个用IO初始化的存储库,而是一个钱包对象(它们是从文件加载的)。为什么在同步解析视图模型时还要进行异步初始化?@Pein从文件加载就是IO。尝试延迟加载,这些问题就会消失。
.Result
应该避免,因为它包含异常。使用:
.ConfigureAwait(false).GetAwaiter().GetResult()取而代之。源代码:有效的代码,但只有当您被无法修改的第三方代码“强迫”使用时,才应该这样做。对象图的构造应该遵循简单的构造函数语义,并且尽可能地同步和快速地完成。也阻止异步代码。这是对另一个答案的评论吗?似乎禁止捕获“c”:@astef我的代码在调用lambda并返回
任务后不使用
c
,所以我认为应该可以。
class ViewModel
{
    public ViewModel(Wallet wallet)
    {
        //nothing fancy here
    }
}
cb.RegisterType<WalletRepository>()
  .As<IWalletRepository>()
  .SingleInstance();
cb.Register(c => c.Resolve<IWalletRepository>().CreateAsync(App.WalletPath));
cb.Register(async c => new ViewModel(await c.Resolve<Task<Wallet>>()))
  .SingleInstance();
var container = cb.Build();
var viewModel = await container.Resolve<Task<ViewModel>>();
cb.Register(c => c.Resolve<IWalletRepository>().CreateAsync(App.WalletPath).Result); // nb: blocking call