paint-brush
ASP.NET Core の Autofac ContainerBuilder に関する知っておくべきヒント@devleader
730 測定値
730 測定値

ASP.NET Core の Autofac ContainerBuilder に関する知っておくべきヒント

Dev Leader12m2024/05/10
Read on Terminal Reader

長すぎる; 読むには

ASP.NET Core で Autofac ContainerBuilder を使用して依存性注入を組み込む方法を学びます。このアプローチで何ができて何ができないかを探ります。
featured image - ASP.NET Core の Autofac ContainerBuilder に関する知っておくべきヒント
Dev Leader HackerNoon profile picture
0-item


アプリケーション内で依存性の注入を管理する方法は多数ありますが、さまざまなアプローチの利点と制限を理解することが重要だと思います。推奨されているAutofacServiceProviderFactoryを使用する代わりに、ASP.NET Core で Autofac ContainerBuilder依存性を構造化する主な方法として使用することは、検討できる方法の 1 つです。


この記事では、AutofacServiceProviderFactory の代わりに、ASP.NET Core アプリケーションで Autofac の ContainerBuilder を使用する方法について説明します。依存性注入で利用できる他のアプローチと比較して、このアプローチでできることとできないことについて説明します。


これは、ASP.NET Core 内で Autofac を使用して依存関係の解決を検討するシリーズの一部です。問題が公開されたら、以下のシリーズも必ず含めます。


このシリーズの最後には、ASP.NET Core と Blazor 内のプラグイン アーキテクチャをより自信を持って探索できるようになり、探索できるコンテンツがさらに増えます。


AutofacServiceProviderFactory の問題

公平に言えば、このセクションのタイトルは*ほぼ*クリックベイトです。ASP.NET Core アプリケーションで Autofac を設定するための推奨方法としてAutofacServiceProviderFactoryを使用することは、ほとんどのアプリケーションにとって最適だと思います。依存性注入フレームワークとして Autofac を使用したい開発者の大多数は、この方法ではほとんど問題に遭遇しません。


それは私たちに次の能力を与えてくれます:

  • WebApplicationBuilder (および現時点で利用可能なもの)にアクセスします
  • IConfigurationのインスタンスへのアクセス ( WebApplicationBuilderインスタンスからも利用可能)
  • 最小限のAPIに依存関係を渡す機能


しかし、私にとって大きな問題は、 WebApplicationインスタンスにアクセスできないことです。C# でプラグイン アーキテクチャを構築する場合、特に ASP.NET Core アプリケーションを構築する場合は、ルートを登録するためにWebApplicationインスタンスにアクセスできるようにしたいです。これにより、プラグインから最小限の API を簡単に登録できます。技術的には、便利な構文を取得するためにIEndpointRouteBuilderの実装にアクセスするだけで済みます。

これなしで非最小限の API を登録できますか? もちろんです。同様の構文を提供し、 WebApplicationインスタンスを必要としない別の方法はありますか? おそらくあります。しかし、その問題を回避しようとする代わりに、興味のある依存関係にアクセスできるかどうかを確認したいと思いました。


依存関係コンテナの設定方法に関する計画を変更する時期が来ました。


サンプル ASP.NET Core アプリケーションの調査

共通の基盤を探るために、サンプル アプリケーションを見てみましょう。 前回の記事を読んだことがあれば、これはサンプル天気予報アプリケーションのバリエーションに似ているでしょう。

 using Autofac; using Microsoft.AspNetCore.Mvc; // personal opinion: // I absolutely love having the entry point of my // applications being essentially: // - make my dependencies // - give me the primary dependency // - use it // - ... nothing else :) ContainerBuilder containerBuilder = new(); containerBuilder.RegisterModule<MyModule>(); using var container = containerBuilder.Build(); using var scope = container.BeginLifetimeScope(); var app = scope.Resolve<WebApplication>(); app.Run(); internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } internal sealed class MyModule : Module { protected override void Load(ContainerBuilder containerBuilder) { containerBuilder.RegisterType<DependencyA>().SingleInstance(); containerBuilder.RegisterType<DependencyB>().SingleInstance(); containerBuilder.RegisterType<DependencyC>().SingleInstance(); containerBuilder .Register(ctx => { var builder = WebApplication.CreateBuilder(Environment.GetCommandLineArgs()); return builder; }) .SingleInstance(); containerBuilder .Register(ctx => ctx.Resolve<WebApplicationBuilder>().Configuration) .As<IConfiguration>() .SingleInstance(); containerBuilder .Register(ctx => { var builder = ctx.Resolve<WebApplicationBuilder>(); var app = builder.Build(); app.UseHttpsRedirection(); // FIXME: the problem is that the Autofac ContainerBuilder // was used to put all of these pieces together, // but we never told the web stack to use Autofac as the // service provider. // this means that the minimal API will never be able to // find services off the container. we would need to resolve // them BEFORE the API is called, like in this registration // method itself, from the context that is passed in. //DependencyA dependencyA = ctx.Resolve<DependencyA>(); // FIXME: But... What happens if something wants to take a // dependency on the WebApplication instance itself? Once the // web application has been built, there's no more adding // dependencies to it! var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; app.MapGet( "/weatherforecast", ( [FromServices] DependencyA dependencyA , [FromServices] DependencyB dependencyB , [FromServices] DependencyC dependencyC ) => { var forecast = Enumerable .Range(1, 5) .Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); return forecast; }); return app; }) .SingleInstance(); } } internal sealed class DependencyA( WebApplicationBuilder _webApplicationBuilder); internal sealed class DependencyB( WebApplication _webApplication); internal sealed class DependencyC( IConfiguration _configuration);


このアプローチの 1 つの特徴は、Autofac ContainerBuilderクラスを主要な依存関係コンテナーとして使用すると、エントリ ポイントを次のように構成できる点です。

  • コンテナの作成
  • 依存関係の登録
  • 主な依存関係の解決
  • … メソッドを 1 つ呼び出すだけでアプリが起動します。


これは、私の意見では、理想的なアプリケーション起動コードです。なぜでしょう? それは、ここに戻って変更する必要がないからです。決して。追加するものがいくつあってもです! アセンブリをスキャンして、より多くのモジュールをロードできるからです。

繰り返しますが、これは私の個人的な好みであり、これがすべての人の目標であるべきだと主張しようとしているわけではありません。

ASP.NET Core の Autofac ContainerBuilder の欠陥

もちろん、これは完全に完璧というわけではない別のアプローチです。それでは、Autofac のこの設定で得られない点について説明しましょう。

  • 最小限の API に渡されるサービス解決パラメータは単純に機能しません。構築されたWebApplication 、依存性注入フレームワークとして Autofac を使用するように構成されていません。
  • 前回の記事と同様に、依存関係コンテナー上のWebApplicationインスタンスをまだ取得できません... そのため、これへのアクセスに関しては特に進歩はありませんでした。


でも、大体はこれだけです。欠点のリストはひどいものではありませんが、Autofac ContainerBuilder アプローチは私たちにとって万能の解決策ではありませんでした。それで、私たちは何を得たのでしょうか? 次の点についても説明するのに役立ちます。

ASP.NET Core における Autofac ContainerBuilder の利点

私たちが行うことすべてには長所と短所があります。ASP.NET Core の Autofac ContainerBuilder の問題を確認したので、ここからどのような進歩が得られたかを見てみましょう。


  • WebApplicationBuilderおよびIConfigurationインスタンスに引き続きアクセスできるため、これはAutofacServiceProviderFactoryアプローチを使用する場合と同等の利点です。
  • プログラムへの非常に合理化されたエントリ ポイントを取得できます。これは非常に気に入っています。コンテナーの作成、登録、エントリ ポイント メソッドの解決、これですべてです。
  • 最小限の API は機能しますが、依存関係は機能しません。それでも、最小限の API に必要な依存関係を事前に解決し、メソッド登録時に渡すことができます。コメント付きのコードを参照してください。

プラグインベースの ASP.NET ルートの反転がさらに増える?

Autofac 登録メソッド内で最小限の API を登録できることはわかりましたが、残念ながら、最小限の API 呼び出し自体でコンテナーからの依存関係を直接解決することはできません。依存関係が自動的に解決されるルート定義を処理する、次のような専用クラスを構築することができます。

 internal sealed class WeatherForecastRoutes( DependencyA _dependencyA // FIXME: still can't depend on this because // we can't get the WebApplication //, DependencyB _dependencyB , DependencyC _dependencyC) { private static readonly string[] _summaries = new[] { "Freezing", "Bracing", // ... }; public WeatherForecast[] Forecast() { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), _summaries[Random.Shared.Next(_summaries.Length)] )) .ToArray(); return forecast; } }


このクラスと依存関係がすべて同じコンテナ上にある場合、自動解決が行われます。その後は、次のコードを呼び出すだけです。

 var weatherForecastRoutes = ctx.Resolve<WeatherForecastRoutes>(); app.MapGet("/weatherforecast2", weatherForecastRoutes.Forecast);


これは、プラグインの観点から見ると、まだ少し面倒です。なぜなら、このように登録コードを呼び出すためだけに、すべてのルート クラスを手動で解決する必要があるからです。これはすべて、これらのものがWebApplicationインスタンスへの独自のアクセスを解決できないという事実に起因しています。


しかし、待ってください... 状況を逆にするとどうなるでしょうか? IRegisterRoutesのようなインターフェースを解決して、 WebApplicationインスタンスを渡すことができたらどうなるでしょうか?

 // NOTE: make sure to register WeatherForecastRouteRegistrar on the // autofac container as IRegisterRoutes! internal interface IRegisterRoutes { void RegisterRoutes(WebApplication app); } internal sealed class WeatherForecastRouteRegistrar( WeatherForecastRoutes _weatherForecastRoutes) : IRegisterRoutes { public void RegisterRoutes(WebApplication app) { app.MapGet("/weatherforecast2", _weatherForecastRoutes.Forecast); } } // TODO: add this to the autofac code where the // WebApplication instance is built: foreach (var registrar in ctx.Resolve<IEnumerable<IRegisterRoutes>>()) { registrar.RegisterRoutes(app); }


これで、 WebApplicationインスタンスがプラグインにアクセスできるかどうかを気にする必要さえなくなりました。最初のバージョンは結局それほど制限がなかったのかもしれません。何かいいことを思いついたのかもしれません... ただし、次の記事でこれについてさらに詳しく説明します。


ASP.NET Core での Autofac ContainerBuilder のまとめ

この記事では、通常提案されているAutofacServiceProviderFactoryを使用する代わりに、Autofac ContainerBuilder明示的に使用する方法について説明しました。同様の利点と欠点を確認しましたが、考慮すべき点もいくつかありました。それぞれの方法には、アプリケーションで何を求めているかに応じて長所と短所があります。


興味深いのは、プラグインに向けて作業しようとしている場合、プラグインからWebApplicationインスタンスにアクセスする必要さえなくなるかもしれないということです。最小限の API を重視する場合、これはまだ制限があるかもしれませんが... そうでなければ、興味深い考え方に至っています。

これが役に立ち、さらに学習の機会を探している場合は、毎週無料で配信される私のソフトウェア エンジニアリング ニュースレターの購読を検討し、 YouTube で私の無料ビデオをチェックしてください。