.net core DI中的异步提供程序

wai*_*rit 11 c# dependency-injection async-await asp.net-core-mvc asp.net-core-webapi

我只是想知道async/await在DI期间是否有可能.

执行以下操作,DI无法解析我的服务.

services.AddScoped(async provider => 
{
  var client = new MyClient();
  await client.ConnectAsync();
  return client;
});
Run Code Online (Sandbox Code Playgroud)

以下作品完美无缺.

services.AddScoped(provider => 
{
  var client = new MyClient();
  client.ConnectAsync().Wait();
  return client;
});
Run Code Online (Sandbox Code Playgroud)

Ste*_*ven 17

解析依赖项时,Async/await没有意义,因为:

这意味着所有涉及I/O的内容都应该推迟到构建对象图之后.

因此,不是注入连接MyClient,而是MyClient应该在第一次使用时连接,而不是在创建时连接.

UPDATE

由于您MyClient不是应用程序组件而是第三方组件,这意味着您无法确保它" 首次使用时连接[s] ".

然而,这应该不是问题,因为依赖性倒置原则已经教会我们:

摘要由上层/政策层拥有

这意味着应用程序组件不应直接依赖于第三方组件,而应依赖于应用程序本身定义的抽象.作为组合根的一部分,可以编写实现这些抽象的适配器并使应用程序代码适应第三方库.

这样做的一个重要优点是,您可以控制应用程序组件使用的API,这是成功的关键,因为它允许连接问题完全隐藏在抽象背后.

以下是您的应用程序定制抽象可能如下所示的示例:

public interface IMyAppService
{
    Task<Data> GetData();
    Task SendData(Data data);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这种抽象缺乏一种ConnectAsync方法; 这隐藏在抽象背后.例如,请查看以下适配器:

public sealed class MyClientAdapter : IMyAppService,
    IDisposable
{
    private readonly Lazy<Task<MyClient>> connectedClient;

    public MyClientAdapter()
    {
        this.connectedClient = new Lazy<Task<MyClient>>(async () =>
        {
            var client = new MyClient();
            await client.ConnectAsync();
            return client;
        });
    }

    public async Task<Data> GetData()
    {
        var client = await this.connectedClient.Value;
        return await client.GetData();
    }

    public async Task SendData(Data data)
    {
        var client = await this.connectedClient.Value;
        await client.SendData(data);
    }

    public void Dispose()
    {
        if (this.connectedClient.IsValueCreated)
        {
            this.connectedClient.Value.Dispose();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

适配器从应用程序代码中隐藏有关连接的详细信息.它包装的创建和连接MyClientLazy<T>,它允许客户将只需一次连接,独立于何种顺序GetDataSendData方法被调用,多少次.

现在你可以让你的应用程序组件的依赖IMyAppService,而不是MyClient和注册MyClientAdapterIMyAppService具有适当的生活方式.

  • 嗨@DougLampe,我会反对这一点。_implementation_ 可能需要实现`IDisposable`,但_abstraction_ 肯定不需要,因为消费组件根本不应该调用`Dispose`。由于只有 DI Container 应该处理这个适配器,所以只有在实现时才需要 `IDisposable`。通常,在抽象上实现“IDisposable”会导致抽象泄漏。 (2认同)
  • 这是一个非常酷的模式。但是,如果我想在应用程序启动时运行“ConnectAsync()”而不是即时运行(我试图避免初始连接上低延迟操作的延迟和超时),会发生什么? (2认同)
  • @cah1r:我反对用“IDisposable”接口标记“IMyAppService”*抽象*。让“MyClientAdapter”类实现“IDisposable”等*实现*是完全可以的。 (2认同)