.NET Core DI 中的异步提供程序

Posted

技术标签:

【中文标题】.NET Core DI 中的异步提供程序【英文标题】:Async provider in .NET Core DI 【发布时间】:2017-08-31 14:41:13 【问题描述】:

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

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

services.AddScoped(async provider => 

  var client = new MyClient();
  await client.ConnectAsync();
  return client;
);

下面的工作非常好。

services.AddScoped(provider => 

  var client = new MyClient();
  client.ConnectAsync().Wait();
  return client;
);

【问题讨论】:

【参考方案1】:

虽然理论上可以在对象解析过程中使用async/await,但在解析依赖关系时并没有多大意义,因为:

Constructors can't be asynchronous,和 对象图的构造应该是simple, reliable and fast

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

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

由于您的MyClient 不是应用程序组件,而是第三方组件,这意味着您无法确保它在使用时“连接[s] 第一次。”

不过,这应该不是问题,因为 Dependency Inversion Principle 已经告诉我们:

摘要归上层/政策层所有

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

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

以下是为应用量身定制的抽象的示例:

public interface IMyAppService

    Task<Data> GetData();
    Task SendData(Data data);

请注意,此抽象缺少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();
        
    

适配器对应用程序代码隐藏连接细节。它将MyClient 的创建和连接包装在Lazy&lt;T&gt; 中,这允许客户端仅连接一次,而与调用GetDataSendData 方法的顺序以及调用次数无关。

这允许您让您的应用程序组件依赖于IMyAppService 而不是MyClient,并将MyClientAdapter 注册为IMyAppService 并使用适当的生活方式。

【讨论】:

嗯!关于那个。恐怕我可能会遇到问题,因为我不能保证第一种方法会调用MyClient 为什么会这样? 哦!对。我明白你的意思。我想这是因为我这里可能有一个特殊情况。我有两种连续调用的方法(即先读后写)。我必须考虑是否有一个用例,我只需要调用第二种方法(目前将失败)。 嗨@DougLampe,我反对这一点。 implementation 可能需要实现IDisposable,但abstraction 肯定不需要,因为消费组件根本不能调用Dispose。由于只有 DI Container 应该处理这个适配器,所以 IDisposable 只在实现时需要。一般来说,在抽象上实现IDisposable 会导致抽象泄漏。 这是一个非常酷的模式。但是,如果我想在应用程序启动时而不是即时运行 ConnectAsync() 会发生什么(我试图在初始连接上避免低延迟操作的延迟和超时)

以上是关于.NET Core DI 中的异步提供程序的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 中的异步延续在哪里排队?

在 ASP.Net Core 应用程序启动期间运行异步代码

ASP.Net Core Web Api 中的异步视频流不起作用

在 .NET / .NET Core 中的异步 I/O 期间,线程池的完成端口线程如何表现?

.NET Core的ZooKeeper异步客户端(支持断线重连永久watcher递归操作并且能跨平台)

ASP.NET Core 依赖注入(DI)