autofac/Autofac.Extensions.DependencyInjection

Configure AutoFac in ASP.NET Core 3.0 Preview 5 or higher

IvanFarkas opened this issue · 3 comments

Configure AutoFac in ASP.NET Core 3.0 Preview 5 or higher

Following the AutoFac documentation, I was able to use AutoFac in ASP.NET Core 3.0 Preview 3. ASP.NET Core 3.0 Preview 4 and ASP.NET Core 3.0 Preview 5 introduced breaking changes and AutoFac no longer works. My controller methods return runtime errors.

The differences between ASP.NET Core 3.0 Preview 3 and ASP.NET Core 3.0 Preview 5 in my code is as follows:
IWebHostBuilder -> IHostBuilder
CreateWebHostBuilder -> CreateHostBuilder
WebHost.CreateDefaultBuilder(args) -> Host.CreateDefaultBuilder(args)

public IServiceProvider ConfigureServices(IServiceCollection services)

to

public void ConfigureServices(IServiceCollection services)

Runtime Error

System.AggregateException
  HResult=0x80131500
  Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: API.Controllers.CityController Lifetime: Transient ImplementationType: CityController': Unable to resolve service for type 'IUnitOfWork' while attempting to activate 'CityController'.) (Error while validating the service descriptor 'ServiceType: LocationController Lifetime: Transient ImplementationType: LocationController': Unable to resolve service for type 'IUnitOfWork' while attempting to activate 'LocationController'.) (Error while validating the service descriptor 'ServiceType: PersonController Lifetime: Transient ImplementationType: PersonController': Unable to resolve service for type 'IUnitOfWork' while attempting to activate 'PersonController'.) (Error while validating the service descriptor 'ServiceType: SchoolController Lifetime: Transient ImplementationType: SchoolController': Unable to resolve service for type 'IUnitOfWork' while attempting to activate 'SchoolController'.) (Error while validating the service descriptor 'ServiceType: TestParentController Lifetime: Transient ImplementationType: TestParentController': Unable to resolve service for type 'IUnitOfWork' while attempting to activate 'TestParentController'.) (Error while validating the service descriptor 'ServiceType: TypeController Lifetime: Transient ImplementationType: TypeController': Unable to resolve service for type 'IUnitOfWork' while attempting to activate 'TypeController'.)
  Source=Microsoft.Extensions.DependencyInjection
  StackTrace:
   at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
   at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
   at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
   at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at API.Program.Main(String[] args) in C:\Projects\FirstResponse\API\Program.cs:line 13

Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: CityController Lifetime: Transient ImplementationType: CityController': Unable to resolve service for type 'IUnitOfWork' while attempting to activate 'CityController'.

Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'IUnitOfWork' while attempting to activate 'CityController'.

Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
  Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
      webBuilder
        .UseStartup<Startup>()
        .ConfigureLogging((hostingContext, builder) =>
        {
          builder.ClearProviders();
          builder.SetMinimumLevel(LogLevel.Trace);
          builder.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
          builder.AddConsole();
          builder.AddDebug();
        })
        .UseNLog();
    });

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
  services
    .AddCustomOptions(Configuration)
    .AddCors()
    .AddJwtAuthentication()
    .AddHttpClients()
    .AddCustomMVC()
    .AddIIS()
    .AddCaching()
    .AddCustomDbContext(Configuration, Environment)
    .AddSwagger()
    .AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies())
    .AddHealthChecksUI();

  var serviceProvider = services.BuildServiceProvider();

  _appSettings = serviceProvider.GetService<IOptionsSnapshot<AppSettings>>().Value;

  var connectionString = Configuration.GetConnectionString(nameof(FirstResponseContext));

  // Create a Autofac container builder
  var builder = new ContainerBuilder();

  // Read service collection to Autofac
  builder.Populate(services);

  // Use and configure Autofac
  builder.RegisterModule(new MediatorModule());
  builder.RegisterModule(new ApplicationModule(connectionString));

  // build the Autofac container
  ApplicationContainer = builder.Build();

  // creating the IServiceProvider out of the Autofac container
  //return new AutofacServiceProvider(ApplicationContainer);
}

ApplicationModule.cs

protected override void Load(ContainerBuilder builder)
{
  builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
  builder.RegisterType<ModelValidationAttribute>().InstancePerLifetimeScope();
  builder.RegisterType<ETagCache>().InstancePerLifetimeScope();

  builder.RegisterType<FirstResponseContext>().As<DbContext>().As<IUnitOfWork>().InstancePerLifetimeScope();
}

You didn't finish following the docs. Notice how you're registering things and building the container... but not attaching the container to the actual Microsoft DI system. The point of returning a new AutofacServiceProvider is to connect Autofac to Microsoft DI. That's not happening.

Go back and check out the docs - you need to use ConfigureContainer to register Autofac stuff. ConfigureServices is only to put stuff into a Microsoft IServiceCollection.

In ASP.NET Core 3.0 Preview 3 the following worked

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  . . . 
  // Creating the IServiceProvider out of the Autofac container. Valid in ASP.NET Core 3.0 Preview 3
  // Not OK in ASP.NET Core 3.0 Preview 5, since "ConfigureServices returning an System.IServiceProvider isn't supported"
  return new AutofacServiceProvider(ApplicationContainer);
}

In ASP.NET Core 3.0 Preview 5 there is a runtime error

System.NotSupportedException
  HResult=0x80131515
  Message=ConfigureServices returning an System.IServiceProvider isn't supported.
  Source=Microsoft.AspNetCore.Hosting
  StackTrace:
   at Microsoft.AspNetCore.Hosting.Internal.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.Internal.GenericWebHostBuilder.<>c__DisplayClass12_0.<UseStartup>b__0(HostBuilderContext context, IServiceCollection services)
   at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
   at FR.API.Program.Main(String[] args) in C:\Projects\FirstResponse\FR.API\Program.cs:line 13

I am reopening this since a solution was NOT provided by @tillig

Solution is in the docs. Notice one way to integrate returns void, not IServiceProvider. "Quick Start (With ConfigureContainer)" Important but subtle difference. If you still can't figure it out, hit StackOverflow up - this isn't a bug, it's a "how do I..." question that is already documented.