paint-brush
Consejos necesarios para Autofac ContainerBuilder en ASP.NET Corepor@devleader
453 lecturas
453 lecturas

Consejos necesarios para Autofac ContainerBuilder en ASP.NET Core

por Dev Leader12m2024/05/10
Read on Terminal Reader

Demasiado Largo; Para Leer

Aprenda a utilizar Autofac ContainerBuilder en ASP.NET Core para conectar la inyección de dependencias. ¡Exploraré lo que podemos y no podemos hacer con este enfoque!
featured image - Consejos necesarios para Autofac ContainerBuilder en ASP.NET Core
Dev Leader HackerNoon profile picture
0-item


Hay muchas formas de gestionar la inyección de dependencias dentro de nuestras aplicaciones y creo que es importante comprender los beneficios y las limitaciones de los diferentes enfoques. ¡Usar un Autofac ContainerBuilder en ASP.NET Core como forma principal de estructurar sus dependencias en lugar de usar el AutofacServiceProviderFactory sugerido es una de esas rutas que podemos explorar!


En este artículo, destaco cómo usar ContainerBuilder de Autofac en su aplicación ASP.NET Core en lugar de AutofacServiceProviderFactory. Veremos lo que puede y no puede hacer con este enfoque en comparación con los otros enfoques a los que tenemos acceso con la inyección de dependencia.


Esto será parte de una serie en la que exploro la resolución de dependencias con Autofac dentro de ASP.NET Core. Me aseguraré de incluir la serie a continuación a medida que se publiquen los números:


Al final de esta serie, podrá explorar con más confianza las arquitecturas de complementos dentro de ASP.NET Core y Blazor, lo que le brindará aún más contenido para explorar.


El problema con AutofacServiceProviderFactory

Para ser justos, el título de la sección es *casi* cebo de clics. Creo que el uso de AutofacServiceProviderFactory como forma sugerida de configurar Autofac en sus aplicaciones ASP.NET Core es excelente para la mayoría de las aplicaciones. La gran mayoría de los desarrolladores que quieran utilizar Autofac como su marco de inyección de dependencia preferido no se encontrarían con muchos problemas de esta manera.


Nos brinda la capacidad de:

  • Acceda a WebApplicationBuilder (y a cualquier cosa disponible en este momento)
  • Acceso a la instancia de IConfiguration (también disponible desde la instancia WebApplicationBuilder )
  • Capacidad para pasar dependencias a API mínimas


Pero el gran problema para mí: no podemos acceder a la instancia WebApplication . Cuando construyo arquitecturas de complementos en C#, particularmente cuando construyo aplicaciones ASP.NET Core, me gusta tener acceso a la instancia WebApplication para registrar rutas. Esto me permite registrar API mínimas de mis complementos con facilidad, lo que técnicamente solo necesita acceso a una implementación de IEndpointRouteBuilder para obtener la sintaxis útil.

¿Puedo registrar API no mínimas sin esto? Absolutamente. ¿Existe otra forma de proporcionar una sintaxis similar y no requerir una instancia WebApplication ? Muy probable. Pero en lugar de intentar solucionar ESE problema, quería ver si podía acceder a la dependencia que me interesa.


¡Era hora de cambiar el plan sobre cómo configurar mi contenedor de dependencias!


Exploración de una aplicación ASP.NET Core de muestra

Veamos una aplicación de ejemplo para tener puntos en común que explorar. Si ha leído el artículo anterior , este será similar: una variación de la aplicación meteorológica de muestra:

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


Una característica de este enfoque es que usar la clase Autofac ContainerBuilder como nuestro contenedor de dependencia principal nos brinda la oportunidad de estructurar nuestro punto de entrada para simplemente:

  • Creación de contenedores
  • Registro de dependencia
  • Resolución de dependencia primaria
  • … ¡Llame a un único método para iniciar la aplicación!


Este es, en mi opinión, el código de inicio de aplicación ideal. ¿Por qué? Porque nunca necesitarás volver aquí para tocarlo. Alguna vez. ¡No importa cuántas cosas agregues! Y eso es todo porque puedes escanear ensamblajes para cargar más módulos.

Nuevamente, esta es una preferencia personal mía y no estoy tratando de afirmar que éste deba ser el objetivo de todos.

Los defectos de Autofac ContainerBuilder en ASP.NET Core

Por supuesto, hay otro enfoque que no es del todo infalible. Así que analicemos lo que NO obtenemos con esta configuración de Autofac:

  • Los parámetros resueltos por el servicio pasados mediante API mínimas simplemente no funcionan. ¡La WebApplication que se creó no está configurada para usar Autofac como marco de inyección de dependencia!
  • Al igual que en el artículo anterior, todavía no podemos obtener la instancia WebApplication en el contenedor de dependencia... Así que no hicimos ningún avance específicamente en el acceso a eso.


¡Pero eso es todo! No es una lista terrible de inconvenientes, pero el enfoque de Autofac ContainerBuilder no fue una solución milagrosa para nosotros. Entonces, ¿qué obtuvimos de esto? también ayudará a explicar lo siguiente:

Los beneficios de Autofac ContainerBuilder en ASP.NET Core

Pros y contras de todo lo que hacemos! Ahora que hemos visto los problemas con Autofac ContainerBuilder en ASP.NET Core, es hora de ver qué avances obtuvimos de esto:


  • Todavía podemos acceder a las instancias WebApplicationBuilder e IConfiguration , por lo que es un beneficio comparable al uso del enfoque AutofacServiceProviderFactory .
  • Podemos obtener un punto de entrada muy simplificado a nuestro programa, lo cual realmente me gusta ver. Creación de contenedores, registro, resolución de su método de punto de entrada, ¡y eso es todo!
  • Las API mínimas funcionan, pero no con dependencias. Aún así, podemos resolver previamente las dependencias que desean las API mínimas y pasarlas en el momento del registro del método. ¡Mira el código comentado!

¿Más inversión para rutas ASP.NET basadas en complementos?

Vimos que podíamos registrar API mínimas dentro de un método de registro de Autofac, pero desafortunadamente, no podemos resolver las dependencias del contenedor directamente en la llamada API mínima. Podríamos crear una clase dedicada como la siguiente que maneje las definiciones de rutas y las dependencias se resuelvan automáticamente:

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


La resolución automática ocurre si tenemos esta clase Y todas las dependencias en el mismo contenedor. Entonces es sólo cuestión de llamar a este código:

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


Esto todavía apesta un poco desde la perspectiva del complemento porque necesitaríamos resolver todas las clases de ruta manualmente solo para llamar al código de registro de esa manera, todo debido al hecho de que estas cosas no pueden resolver su propio acceso a la WebApplication instancia.


Pero espera… ¿Qué pasa si le damos la vuelta a las cosas? ¿Qué pasaría si pudiéramos resolver alguna interfaz como IRegisterRoutes y pasar la instancia 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); }


Ahora, ¡ni siquiera necesitamos preocuparnos si nuestros complementos pueden acceder a la instancia WebApplication ! Quizás la primera versión no fuera tan limitante después de todo. Quizás estemos en algo aquí... Sin embargo, el próximo artículo debería explicar esto con más detalle.


Conclusión de Autofac ContainerBuilder en ASP.NET Core

En este artículo, exploré el uso de Autofac ContainerBuilder explícitamente en lugar de usar AutofacServiceProviderFactory como se sugiere normalmente. Vimos algunos beneficios y desventajas similares, pero también un conjunto diferente de cosas a considerar. Cada forma puede ofrecer ventajas y desventajas dependiendo de lo que busque en su aplicación.


Lo interesante fue que si intentamos trabajar con complementos, es posible que ni siquiera necesitemos acceder a la instancia WebApplication desde nuestros complementos. Si nos preocupamos por las API mínimas, esto aún podría ser limitante... pero por lo demás, ¡estamos en una línea de pensamiento interesante!

Si esto le resultó útil y está buscando más oportunidades de aprendizaje, considere suscribirse a mi boletín semanal gratuito de ingeniería de software y vea mis videos gratuitos en YouTube .