web-dev-qa-db-ja.com

.netコア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;
});
15
waitforit

Async/awaitは、依存関係を解決するときに意味がありません。

これは、オブジェクトグラフが構築されるまで、I/Oに関連するすべてを延期する必要があることを意味します。

したがって、接続されたMyClientをインジェクトする代わりに、MyClientは、それが作成されたときではなく、初めてusedされたときに接続する必要があります。

[〜#〜]更新[〜#〜]

あなたの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>にラップします。これにより、GetDataSendDataの順序に関係なく、クライアントを1回だけ接続できますメソッドが呼び出され、その回数。

これで、アプリケーションコンポーネントをIMyAppServiceではなくMyClientに依存させ、MyClientAdapterIMyAppServiceとして適切なライフスタイルに登録できます。

17
Steven