【问题标题】:Async provider in .NET Core DI.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;
});

【问题讨论】:

    标签: c# dependency-injection async-await asp.net-core-mvc asp.net-core-webapi


    【解决方案1】:

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

    这意味着所有涉及 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() 会发生什么(我试图在初始连接上避免低延迟操作的延迟和超时)
    猜你喜欢
    • 2021-05-16
    • 2021-06-11
    • 2021-09-26
    • 1970-01-01
    • 1970-01-01
    • 2020-09-10
    • 2021-04-14
    • 2017-07-28
    • 1970-01-01
    相关资源
    最近更新 更多