.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<T>
中,这允许客户端仅连接一次,而与调用GetData
和SendData
方法的顺序以及调用次数无关。
这允许您让您的应用程序组件依赖于IMyAppService
而不是MyClient
,并将MyClientAdapter
注册为IMyAppService
并使用适当的生活方式。
【讨论】:
嗯!关于那个。恐怕我可能会遇到问题,因为我不能保证第一种方法会调用MyClient
。
为什么会这样?
哦!对。我明白你的意思。我想这是因为我这里可能有一个特殊情况。我有两种连续调用的方法(即先读后写)。我必须考虑是否有一个用例,我只需要调用第二种方法(目前将失败)。
嗨@DougLampe,我反对这一点。 implementation 可能需要实现IDisposable
,但abstraction 肯定不需要,因为消费组件根本不能调用Dispose
。由于只有 DI Container 应该处理这个适配器,所以 IDisposable
只在实现时需要。一般来说,在抽象上实现IDisposable
会导致抽象泄漏。
这是一个非常酷的模式。但是,如果我想在应用程序启动时而不是即时运行 ConnectAsync()
会发生什么(我试图在初始连接上避免低延迟操作的延迟和超时)以上是关于.NET Core DI 中的异步提供程序的主要内容,如果未能解决你的问题,请参考以下文章
ASP.Net Core Web Api 中的异步视频流不起作用
在 .NET / .NET Core 中的异步 I/O 期间,线程池的完成端口线程如何表现?