Поставщик async в .net core DI

Мне просто интересно, возможно ли иметь async/await во время DI.

Выполняя следующее, 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 не имеет смысла при разрешении зависимостей, потому что:

Это означает, что все, что связано с вводом/выводом, должно быть отложено до тех пор, пока не будет построен граф объектов.

Таким образом, вместо внедрения подключенного 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 с соответствующим образом жизни.