paint-brush
Precisa saber dicas para Autofac ContainerBuilder no ASP.NET Corepor@devleader
760 leituras
760 leituras

Precisa saber dicas para Autofac ContainerBuilder no ASP.NET Core

por Dev Leader12m2024/05/10
Read on Terminal Reader

Muito longo; Para ler

Aprenda como usar o Autofac ContainerBuilder no ASP.NET Core para conectar a injeção de dependência. Explorarei o que podemos e o que não podemos fazer com essa abordagem!
featured image - Precisa saber dicas para Autofac ContainerBuilder no ASP.NET Core
Dev Leader HackerNoon profile picture
0-item


Há muitas maneiras de gerenciar a injeção de dependência dentro de nossos aplicativos e acho importante compreender os benefícios e as limitações de diferentes abordagens. Usar um Autofac ContainerBuilder no ASP.NET Core como a principal forma de estruturar suas dependências em vez de usar o AutofacServiceProviderFactory sugerido é um caminho que podemos explorar!


Neste artigo, destaco como usar o ContainerBuilder do Autofac em seu aplicativo ASP.NET Core em vez do AutofacServiceProviderFactory. Veremos o que você pode ou não fazer com essa abordagem em comparação com outras abordagens às quais temos acesso com injeção de dependência.


Isso fará parte de uma série em que exploro a resolução de dependências com Autofac dentro do ASP.NET Core. Com certeza incluirei a série abaixo à medida que as edições forem publicadas:


Ao final desta série, você poderá explorar com mais confiança as arquiteturas de plug-ins dentro do ASP.NET Core e do Blazor, o que fornecerá ainda mais conteúdo para você explorar.


O problema com AutofacServiceProviderFactory

Para ser justo, o título da seção é *quase* uma isca de clique. Eu realmente acho que AutofacServiceProviderFactory usado como a maneira sugerida de configurar o Autofac em seus aplicativos ASP.NET Core é ótimo para a maioria dos aplicativos. A grande maioria dos desenvolvedores que desejam usar o Autofac como estrutura de injeção de dependência preferida não enfrentaria muitos problemas dessa maneira.


Isso nos permite a capacidade de:

  • Acesse o WebApplicationBuilder (e qualquer coisa disponível neste momento)
  • Acesso à instância de IConfiguration (também disponível na instância WebApplicationBuilder )
  • Capacidade de passar dependências para APIs mínimas


Mas o grande problema para mim: não podemos acessar a instância WebApplication . Quando construo arquiteturas de plug-ins em C#, principalmente ao criar aplicativos ASP.NET Core, gosto de ter acesso à instância WebApplication para registrar rotas. Isso me permite registrar APIs mínimas de meus plug-ins com facilidade, que tecnicamente só precisam de acesso a uma implementação de IEndpointRouteBuilder para obter a sintaxe útil.

Posso registrar APIs não mínimas sem isso? Absolutamente. Existe outra maneira de fornecer sintaxe semelhante e não exigir uma instância WebApplication ? Muito provavelmente. Mas, em vez de tentar contornar ESSE problema, eu queria ver se conseguia acessar a dependência na qual estou interessado.


Era hora de mudar o plano de configuração do meu contêiner de dependência!


Explorando um exemplo de aplicativo ASP.NET Core

Vejamos um exemplo de aplicativo para que tenhamos alguns pontos em comum a explorar. Se você leu o artigo anterior , isso será semelhante — uma variação do aplicativo de amostra de clima:

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


Um destaque dessa abordagem é que usar a classe Autofac ContainerBuilder como nosso contêiner de dependência principal nos dá a oportunidade de estruturar nosso ponto de entrada apenas para:

  • Criação de contêiner
  • Registro de dependência
  • Resolução de dependência primária
  • … Chame um único método para iniciar o aplicativo!


Este é, na minha opinião, o código de inicialização de aplicativo ideal. Por que? Porque você nunca precisa voltar aqui para tocá-lo. Sempre. Não importa quantas coisas você adicione! E isso tudo porque você pode digitalizar montagens para carregar mais módulos.

Mais uma vez, esta é uma preferência pessoal minha e não estou tentando afirmar que este deva ser o objetivo de todos.

As falhas do Autofac ContainerBuilder no ASP.NET Core

Claro, outra abordagem que não é totalmente à prova de balas. Então, vamos discutir o que NÃO obtemos com esta configuração do Autofac:

  • Parâmetros resolvidos pelo serviço transmitidos em APIs mínimas simplesmente não funcionam. O WebApplication que foi construído não está configurado para usar Autofac como estrutura de injeção de dependência!
  • Assim como no artigo anterior, ainda não conseguimos obter a instância WebApplication no contêiner de dependência… Portanto, não fizemos nenhum avanço específico no acesso a ela.


Mas é principalmente isso! Não é uma lista terrível de desvantagens, mas a abordagem Autofac ContainerBuilder não foi uma solução mágica para nós. Então, o que ganhamos com isso? também ajudará a explicar o seguinte:

Os benefícios do Autofac ContainerBuilder no ASP.NET Core

Prós e contras de tudo o que fazemos! Agora que vimos os problemas com o Autofac ContainerBuilder no ASP.NET Core, é hora de ver quais avanços obtivemos com isso:


  • Ainda podemos acessar as instâncias WebApplicationBuilder e IConfiguration , portanto esse é um benefício comparável ao uso da abordagem AutofacServiceProviderFactory .
  • Podemos obter um ponto de entrada muito simplificado para o nosso programa, o que eu realmente gosto de ver. Criação de contêiner, registro, resolução do método de ponto de entrada e pronto!
  • APIs mínimas funcionam, mas não com dependências. Ainda assim, podemos pré-resolver as dependências que as APIs mínimas desejam e passá-las no momento do registro do método. Veja o código comentado!

Mais inversão para rotas ASP.NET baseadas em plug-ins?

Vimos que poderíamos registrar APIs mínimas dentro de um método de registro Autofac, mas infelizmente não podemos resolver dependências do contêiner diretamente na própria chamada de API mínima. Poderíamos construir uma classe dedicada como a seguinte, que lida com definições de rotas com dependências sendo resolvidas automaticamente:

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


A resolução automática acontece se tivermos esta classe E todas as dependências no mesmo container. Então é só chamar esse código:

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


Isso ainda é um pouco chato do ponto de vista do plug-in, porque precisaríamos resolver todas as classes de rota manualmente apenas para chamar o código de registro dessa forma - tudo decorrente do fato de que essas coisas não podem resolver seu próprio acesso ao WebApplication instância.


Mas espere… E se invertermos as coisas? E se pudéssemos resolver alguma interface como IRegisterRoutes e passar a instância 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); }


Agora, nem precisamos nos preocupar se a instância WebApplication está acessível aos nossos plugins! Talvez a primeira versão não fosse tão limitante, afinal. Talvez estejamos no caminho certo… Porém, o próximo artigo deverá explicar isso com mais detalhes.


Concluindo o Autofac ContainerBuilder no ASP.NET Core

Neste artigo, explorei o uso explícito de um Autofac ContainerBuilder em vez de usar AutofacServiceProviderFactory como normalmente é sugerido. Vimos alguns benefícios e desvantagens semelhantes, mas também um conjunto diferente de coisas a considerar. Cada forma pode oferecer prós e contras, dependendo do que você procura em seu aplicativo.


O interessante é que se estivermos tentando trabalhar com plug-ins, talvez nem precisemos acessar a instância WebApplication de nossos plug-ins! Se nos preocupamos com APIs mínimas, isso ainda pode ser limitante... mas por outro lado, estamos em uma linha de pensamento interessante!

Se você achou isso útil e está procurando mais oportunidades de aprendizado, considere assinar meu boletim informativo semanal gratuito sobre engenharia de software e conferir meus vídeos gratuitos no YouTube !