paint-brush
Wissenswerte Tipps für Autofac ContainerBuilder in ASP.NET Corevon@devleader
453 Lesungen
453 Lesungen

Wissenswerte Tipps für Autofac ContainerBuilder in ASP.NET Core

von Dev Leader12m2024/05/10
Read on Terminal Reader

Zu lang; Lesen

Erfahren Sie, wie Sie Autofac ContainerBuilder in ASP.NET Core verwenden, um die Abhängigkeitsinjektion zu aktivieren. Ich werde untersuchen, was wir mit diesem Ansatz tun können und was nicht!
featured image - Wissenswerte Tipps für Autofac ContainerBuilder in ASP.NET Core
Dev Leader HackerNoon profile picture
0-item


Es gibt viele Möglichkeiten, die Abhängigkeitsinjektion in unseren Anwendungen zu verwalten, und ich denke, es ist wichtig, die Vorteile und Einschränkungen verschiedener Ansätze zu verstehen. Die Verwendung eines Autofac ContainerBuilder in ASP.NET Core als primäre Methode zum Strukturieren Ihrer Abhängigkeiten anstelle der vorgeschlagenen AutofacServiceProviderFactory ist ein solcher Weg, den wir erkunden können!


In diesem Artikel erläutere ich, wie Sie den ContainerBuilder von Autofac in Ihrer ASP.NET Core-Anwendung anstelle der AutofacServiceProviderFactory verwenden. Wir werden uns ansehen, was Sie mit diesem Ansatz im Vergleich zu den anderen Ansätzen tun können, auf die wir mit Abhängigkeitsinjektion Zugriff haben, und was nicht.


Dies ist Teil einer Reihe, in der ich die Abhängigkeitsauflösung mit Autofac in ASP.NET Core untersuche. Ich werde die folgende Reihe auf jeden Fall hinzufügen, sobald die Ausgaben veröffentlicht werden:


Am Ende dieser Reihe können Sie die Plugin-Architekturen in ASP.NET Core und Blazor sicherer erkunden, wodurch Ihnen noch mehr Inhalte zum Erkunden zur Verfügung stehen.


Das Problem mit AutofacServiceProviderFactory

Um ehrlich zu sein, ist der Abschnittstitel *fast* ein Klickköder. Ich denke, dass die Verwendung von AutofacServiceProviderFactory als empfohlene Methode zum Einrichten von Autofac in Ihren ASP.NET Core-Anwendungen für die meisten Anwendungen großartig ist. Die große Mehrheit der Entwickler, die Autofac als bevorzugtes Framework für die Abhängigkeitsinjektion verwenden möchten, würde auf diese Weise überhaupt nicht auf viele Probleme stoßen.


Es bietet uns die Möglichkeit:

  • Greifen Sie auf den WebApplicationBuilder zu (und auf alles, was zu diesem Zeitpunkt verfügbar ist).
  • Zugriff auf die Instanz von IConfiguration (auch außerhalb der WebApplicationBuilder Instanz verfügbar)
  • Möglichkeit, Abhängigkeiten an minimale APIs weiterzugeben


Aber das große Problem für mich: Wir können nicht auf die WebApplication Instanz zugreifen. Wenn ich Plugin-Architekturen in C# erstelle, insbesondere beim Erstellen von ASP.NET Core-Anwendungen, möchte ich Zugriff auf die WebApplication Instanz haben, um Routen zu registrieren. Auf diese Weise kann ich problemlos minimale APIs von meinen Plugins registrieren, die technisch nur Zugriff auf eine Implementierung von IEndpointRouteBuilder benötigen, um die praktische Syntax zu erhalten.

Kann ich nicht-minimale APIs ohne dies registrieren? Absolut. Gibt es eine andere Möglichkeit, eine ähnliche Syntax bereitzustellen und keine WebApplication Instanz zu benötigen? Sehr wahrscheinlich. Aber anstatt zu versuchen, DIESES Problem zu umgehen, wollte ich sehen, ob ich einfach auf die Abhängigkeit zugreifen kann, an der ich interessiert bin.


Es war Zeit, den Plan zur Einrichtung meines Abhängigkeitscontainers zu ändern!


Erkunden einer Beispielanwendung für ASP.NET Core

Schauen wir uns eine Beispielanwendung an, damit wir eine gemeinsame Basis haben, die wir untersuchen können. Wenn Sie den vorherigen Artikel gelesen haben, wird dies ähnlich aussehen – eine Variation der Beispiel-Wetteranwendung:

 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);


Ein Vorteil dieses Ansatzes besteht darin, dass die Verwendung der Autofac ContainerBuilder -Klasse als primären Abhängigkeitscontainer uns die Möglichkeit bietet, unseren Einstiegspunkt wie folgt zu strukturieren:

  • Containererstellung
  • Abhängigkeitsregistrierung
  • Auflösung primärer Abhängigkeiten
  • … Rufen Sie eine einzelne Methode auf, um die App zu starten!


Das ist meiner Meinung nach der ideale Startcode für eine Anwendung. Warum? Weil Sie nie wieder hierher zurückkommen müssen, um ihn zu bearbeiten. Niemals. Egal, wie viele Dinge Sie hinzufügen! Und das alles, weil Sie Assemblys scannen können, um weitere Module zu laden.

Auch hier handelt es sich um eine persönliche Vorliebe von mir und ich möchte nicht behaupten, dass dies jedermanns Ziel sein sollte.

Die Mängel von Autofac ContainerBuilder in ASP.NET Core

Natürlich gibt es auch einen anderen Ansatz, der nicht ganz narrensicher ist. Lassen Sie uns also besprechen, was wir mit diesem Setup von Autofac NICHT bekommen:

  • Über minimale APIs übergebene, vom Dienst aufgelöste Parameter funktionieren einfach nicht. Die erstellte WebApplication ist nicht für die Verwendung von Autofac als Framework für die Abhängigkeitsinjektion konfiguriert!
  • Wie im vorherigen Artikel können wir die WebApplication Instanz immer noch nicht auf den Abhängigkeitscontainer abrufen. Wir haben also keine speziellen Fortschritte beim Zugriff darauf gemacht.


Aber das ist im Großen und Ganzen auch schon alles! Die Liste der Nachteile ist nicht schrecklich, aber der Autofac ContainerBuilder-Ansatz war für uns keine Wunderlösung. Was haben wir also daraus gelernt? hilft auch dabei, Folgendes zu erklären:

Die Vorteile von Autofac ContainerBuilder in ASP.NET Core

Vor- und Nachteile für alles, was wir tun! Nachdem wir uns nun die Probleme mit Autofac ContainerBuilder in ASP.NET Core angesehen haben, ist es an der Zeit, uns anzusehen, welche Fortschritte wir daraus gezogen haben:


  • Wir können weiterhin auf die Instanzen von WebApplicationBuilder und IConfiguration zugreifen, was einen vergleichbaren Vorteil wie bei der Verwendung des AutofacServiceProviderFactory Ansatzes darstellt.
  • Wir können einen sehr optimierten Einstiegspunkt für unser Programm erhalten, was ich sehr gerne sehe. Containererstellung, Registrierung, Auflösen Ihrer Einstiegspunktmethode, und das ist alles!
  • Minimale APIs funktionieren, aber nicht mit Abhängigkeiten. Dennoch können wir die von den minimalen APIs gewünschten Abhängigkeiten vorab auflösen und diese bei der Methodenregistrierung übergeben. Siehe den kommentierten Code!

Mehr Inversion für Plugin-basierte ASP.NET-Routen?

Wir haben gesehen, dass wir minimale APIs innerhalb einer Autofac-Registrierungsmethode registrieren konnten, aber leider können wir Abhängigkeiten vom Container nicht direkt beim minimalen API-Aufruf selbst auflösen. Wir könnten eine dedizierte Klasse wie die folgende erstellen, die Routendefinitionen mit automatisch aufgelösten Abhängigkeiten verarbeitet:

 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; } }


Die automatische Auflösung erfolgt, wenn wir diese Klasse UND die Abhängigkeiten alle im selben Container haben. Dann müssen wir nur noch diesen Code aufrufen:

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


Aus Plug-in-Sicht ist das immer noch ein bisschen ärgerlich, weil wir alle Routenklassen manuell auflösen müssten, nur um den Registrierungscode auf diese Weise aufzurufen – und das alles rührt von der Tatsache her, dass diese Dinge ihren eigenen Zugriff auf die WebApplication Instanz nicht auflösen können.


Aber warten Sie mal … Was wäre, wenn wir die Dinge umdrehen würden? Was wäre, wenn wir eine Schnittstelle wie IRegisterRoutes auflösen und die WebApplication Instanz übergeben könnten?!

 // 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); }


Jetzt müssen wir uns nicht einmal mehr darum kümmern, ob die WebApplication Instanz für unsere Plugins zugänglich ist! Vielleicht war die erste Version doch nicht so einschränkend. Vielleicht sind wir hier auf etwas gestoßen … Der nächste Artikel sollte dies jedoch genauer erklären.


Autofac ContainerBuilder in ASP.NET Core abschließen

In diesem Artikel habe ich die Verwendung eines Autofac ContainerBuilder explizit anstelle der normalerweise empfohlenen Verwendung von AutofacServiceProviderFactory untersucht. Wir haben einige ähnliche Vor- und Nachteile gesehen, aber auch einen anderen Satz von Dingen, die zu berücksichtigen sind. Jede Methode kann Vor- und Nachteile bieten, je nachdem, was Sie in Ihrer Anwendung erreichen möchten.


Interessant war, dass wir, wenn wir auf Plugins hinarbeiten, möglicherweise gar nicht auf die WebApplication Instanz unserer Plugins zugreifen müssen! Wenn uns minimale APIs wichtig sind, ist dies möglicherweise immer noch einschränkend … aber ansonsten sind wir auf einem interessanten Denkansatz!

Wenn Sie dies nützlich fanden und nach weiteren Lernmöglichkeiten suchen, abonnieren Sie meinen kostenlosen wöchentlichen Softwareentwicklungs-Newsletter und sehen Sie sich meine kostenlosen Videos auf YouTube an!