虽然在对象解析期间理论上可以使用async/await,但您应考虑以下限制:
由于这些限制,最好将涉及I/O的所有操作推迟到对象图构建之后。
因此,不要注入已连接的MyClient
,而是在第一次使用时连接MyClient
——而不是在创建时连接。
由于您的MyClient
不是一个应用程序组件,而是一个第三方组件,这意味着您无法确保它“在第一次使用时连接”。
这不应该是一个问题,因为依赖倒置原则已经教给我们:
抽象层由上层/策略层拥有
这意味着应用程序组件不应直接依赖于第三方组件,而应该依赖于应用程序本身定义的抽象。作为组合根的一部分,可以编写适配器来实现这些抽象,并将应用程序代码适应到第三方库。
这样做的一个重要优点是,您可以控制应用程序组件使用的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();
}
}
适配器将连接细节从应用程序代码中隐藏。它在
Lazy<T>
中包装了
MyClient
的创建和连接,这使得客户端只需连接一次,而不受调用
GetData
和
SendData
方法的顺序以及次数的影响。
这使您可以让应用程序组件依赖于
IMyAppService
而不是
MyClient
,并将
MyClientAdapter
注册为具有适当生命周期的
IMyAppService
。
IDisposable
,但_抽象_肯定不需要,因为使用组件根本不应该能够调用Dispose
。由于只有DI容器应该处理此适配器的处理,因此在实现中才需要IDisposable
。通常,在抽象上实现IDisposable
会导致泄漏的抽象。 - StevenConnectAsync()
,会发生什么情况?(我试图避免在初始连接上进行低延迟操作时出现超时和延迟) - gabeIMyAppService
这个抽象类上打上IDisposable
接口的标记。像MyClientAdapter
这样的实现类实现IDisposable
是完全可以的。 - Steven