Search code examples
asp.net-coredependency-injectionconsole-applicationautofac

Implementing Autofac in .NET Core 3 Console Application, injecting IServiceProvider into Config class (not working?)


This might be a "stupid" question but I'm kind of lost, I'm trying to implement Autofac into a working .NET Core 3.1 Console Application but can't seem to get some things to work.

I have a class named Config.cs that are used at multiple places the console application so I'd like to keep that file so as to not rewrite this implementation.

Here is that file

public class Config : IConfig
{
    readonly IConfiguration _config;

    public Config(IServiceProvider services)
    {
        _config = (IConfiguration)services.GetService(typeof(IConfiguration));
    }

    public string StorageConnectionString => _config["ConnectionStrings:StorageConnectionString"];
    public string SqlConnectionString => _config["ConnectionStrings:SqlConnectionString"];
    public string MongoConnectionString => _config["ConnectionStrings:MongoConnectionString"];
    public string RayGunApiKey => _config["RayGunApiKey"];
    public string ElasticsearchUri => _config["Uris:ElasticsearchUri"];
    public string StatsDUri => _config["Uris:StatsDUri"];

    public string Get(string key) => _config[key];
}

This is first used in Functions.cs in a TimeTrigger:

public class Functions
{
    readonly IConfig _config;

    public Functions(ILogger<Functions> logger, IConfig config)
    {
        _logger = logger;
        _config = config;
    }


    public void Run([TimerTrigger("00:00:01", RunOnStartup = true, UseMonitor = true)]TimerInfo myTimer)
    {
        ...
    }
}

But when I start the console application an error is thrown

System.InvalidOperationException: Unable to resolve service for type 'DataPurger.IConfig' while attempting to activate 'DataPurger.Functions'.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
   at lambda_method(Closure , IServiceProvider , Object[] )
   at Microsoft.Azure.WebJobs.Host.Executors.DefaultJobActivator.CreateInstance[T](IServiceProvider serviceProvider) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\DefaultJobActivator.cs:line 37
   at Microsoft.Azure.WebJobs.Host.Executors.DefaultJobActivator.CreateInstance[T](IFunctionInstanceEx functionInstance) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\DefaultJobActivator.cs:line 32
   at Microsoft.Azure.WebJobs.Host.Executors.ActivatorInstanceFactory`1.<>c__DisplayClass1_1.<.ctor>b__0(IFunctionInstanceEx i) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\ActivatorInstanceFactory.cs:line 20
   at Microsoft.Azure.WebJobs.Host.Executors.ActivatorInstanceFactory`1.Create(IFunctionInstanceEx functionInstance) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\ActivatorInstanceFactory.cs:line 26
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.CreateInstance(IFunctionInstanceEx functionInstance) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionInvoker.cs:line 44
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ParameterHelper.Initialize() in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 846
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.TryExecuteAsyncCore(IFunctionInstanceEx functionInstance, CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 117

This injection works without Autofac, but I'm "forced" to implement it since Autofac is used in every other project where I'm working.

Here is the Program.cs

static void Main(string[] args)
{
    RegisterServices();

    CreateHostBuilder(args).Build().RunAsync().Wait();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            config.AddEnvironmentVariables();
            config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
            config.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true,
                reloadOnChange: true);
        })
        .ConfigureLogging((context, b) =>
        {
            b.AddConsole();
        })
        .ConfigureServices(s =>
        {
            s.AddLogging();
            s.AddOptions();
        })
        .ConfigureWebJobs(builder =>
        {
            builder.AddAzureStorageCoreServices();
            builder.AddExecutionContextBinding();
            builder.AddTimers();
        })
        .UseConsoleLifetime()
        .UseServiceProviderFactory(new AutofacServiceProviderFactory());

static void RegisterServices()
{
    var collection = new ServiceCollection();
    var builder = new ContainerBuilder();

    builder.RegisterModule<BaseModule>();
    builder.Populate(collection);

    var container = builder.Build();

    _serviceProvider = new AutofacServiceProvider(container);
}

public class BaseModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<Config>().As<IConfig>().SingleInstance();
    }
}

What am I doing wrong, I'm sure there is something missing or that I did not understand correctly?


Solution

  • It is my sample for ASP .Net core 3.1
    in Program.cs I changed

    public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<-like yours
                .UseSerilog()
                .ConfigureLogging((context, builder) => builder.AddSerilog())
                .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
    

    and I added to Startup.cs

    public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterType<QuartzWrapper>().As<IQuartzWrapper>().SingleInstance();
            builder.Register(provider => new JobFactory(provider)).As<IJobFactory>().InstancePerDependency();
            builder.RegisterType<JobClass>().As<IJob>().InstancePerDependency();
    
    
        }
    

    and I used the next packages:

    1. Autofac - Version="5.1.2"
    2. Autofac.Extensions.DependencyInjection - Version="6.0.0"