Miroslav Holec
Premium

Registrace AutoFac v [ASP].NET Core 3.0

Miroslav Holec   12. září 2019

Článek se vztahuje k verzi produktu ASP.NET Core 3.0

Tento článek byl napsán v roce 2019. Vývojářské technologie se neustále inovují a článek již nemusí popisovat aktuální stav technologie, ideální řešení a můj současný pohled na dané téma.

Nový .NET Core 3.0 přináší obecný host, který v sobě zapouzdřuje podporu DI, logování a konfigurace aplikace. Vývojáře by měl zajímat, protože brzy zmizí zpětná kompatibilita s web hostem a přechod na generic host verzi bude nezbytný. Tento host navíc nabízí možnost velmi pohodlného zapojení vlastního DI kontejneru.

ServiceProviderFactory

Když jste chtěli zapojit doposud vlastní kontejner do ASP.NET Core aplikace, museli jste vytvořit jeho instanci v metodě ConfigureServices() a dle návodu nějakým způsobem slít dohromady registrované služby MS DI kontejneru a vašeho custom kontejneru. Nově se tato logika odsouvá do tzv. ServiceProviderFactory, která se pouze registruje do generic hostu. Na příkladu níže je vidět registrace v případě AutoFacu.

Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
    webBuilder.UseStartup<Startup>();
})
.Build().Run();

Samotnou ServiceProviderFactory bude většina DI kontejnerů vydávat jako součást NuGet balíčku. Pokud ne, budete si ji muset napsat svépomocí. V takovém případě stačí implementovat generický interface IServiceProviderFactory.

V případě AutoFacu najdete tuto factory v NuGet balíčku Autofac.Extensions.DependencyInjection.

Registrace services do kontejneru

A teď přichází největší pecka! Použitím ServiceProviderFactory můžete dle konvencí připravit ve třídě Startup.cs metodu ConfigureContainer, která má na vstupu samotný DI kontejner (ten generický typ ze ServiceProviderFactory).

public class Startup
{
    // Microsoft DI
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // Custom DI (AutoFac)
    public void ConfigureContainer(ContainerBuilder builder)
    {
        builder.RegisterType<ForecastService>()
           .As<IForecastService>().InstancePerLifetimeScope();
    }

    // HTTP Context Pipeline
    public void Configure(IApplicationBuilder app, Handlers.Handlers handlers)
    {
    }
}

Metody se odbavují v pořadí ConfigureServices(), poté ConfigureContainer() a nakonec Configure(). Do metody Configure() lze již přes method injection předat vlastní registrované služby.

Pozor na konstruktor třídy Startup

Poslední zmínku bych chtěl věnovat konstruktoru třídy Startup. Do něj mohou s použitím generic hosta nově vstoupit jen dva povolené typy IConfiguration a IWebHostEnvironment.

public class Startup
{
    public Startup(IConfiguration configuration, IWebHostEnvironment env)
    {
        this.configuration = configuration;
        this.env = env;
    }

    private readonly IConfiguration configuration;
    private readonly IWebHostEnvironment env;

    ...

Behind the scene

Pakliže vás zajímá, jak funguje IServiceProviderFactory na pozadí, čtěte dále. Faktorka vyžaduje implementaci dvou metod:

CreateBuilder(IServiceCollection services)

Jedná se o metodu, která na vstupu dostane MS DI kolekci registrovaných služeb. Jsou to ty služby, které registrujete v metodě Startup.ConfigureServices(). V této metodě je nutné tyto služby vzít a nalít je do nově registrovaného kontejneru (nebo nějakou mechanikou kolem něj). V případě AutoFacu:

public ContainerBuilder CreateBuilder(IServiceCollection services)
{
    var builder = new ContainerBuilder();

    builder.Populate(services);

    _configurationAction(builder);

    return builder;
}

CreateServiceProvider(ContainerBuilder builder)

Druhá metoda dostane na vstupu již custom container builder (mohl by to být i přímo kontejner). V této metodě je následně nutné vyrobit instanci pro interface IServiceProvider. Ten by měl podporovat všechny známé metody, jako například GetService() nebo GetRequiredService(). V případě AutoFacu:

public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
{
    var container = containerBuilder.Build();
    return new AutofacServiceProvider(container);
}