Ответ 1
Async/await не имеет смысла при разрешении зависимостей, потому что:
- Конструкторы не могут быть асинхронными и
- Построение графов объектов должно быть простым, надежным и быстрым
Это означает, что все, что связано с вводом/выводом, должно быть отложено до тех пор, пока не будет построен граф объектов.
Таким образом, вместо внедрения подключенного MyClient
, MyClient
должен подключаться при первом использовании, а не при его создании.
UPDATE
Поскольку ваш MyClient
не является компонентом приложения, а сторонним компонентом, это означает, что вы не можете гарантировать, что он "подключится [s], когда он используется в первый раз".
Это не должно быть проблемой, так как принцип обращения зависимостей уже учит нас тому, что:
тезисы принадлежат верхним уровням/уровням политики
Это означает, что компоненты приложения не должны напрямую зависеть от сторонних компонентов, а должны зависеть от абстракций, определенных самим приложением. В рамках 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
с соответствующим образом жизни.