Home > Blockchain >  Dependency Injection in .NET 6 sub-dependencies not injected
Dependency Injection in .NET 6 sub-dependencies not injected

Time:10-04

I am writing a Blazor Web App using Azure App service. I have the basics in place and now am writing a WebJob Handler which is also basically working.

As I was needing some functions written for the main server I decided to extract the the helpers I had written to a seperate class lib so I can use them in the Webjob app. So far so good. All builds and runs, however... One of my helper libs uses AutoMapper to handle DTO and server objects. In the server application, the DI works just fine. Here is the server startup code (extract)..

// Auto Mapper Configurations
var mappingConfig = new MapperConfiguration(mc =>
{
    mc.AddProfile(new MappingProfile());
    mc.AddCollectionMappers();

});

IMapper mapper = mappingConfig.CreateMapper();

builder.Services.AddSingleton(mapper);

... more stuff ...

//AzureHelpers
builder.Services.AddScoped<IAzureHelpers, AzureHelpers>();

//RaffleHelpers
builder.Services.AddScoped<IRaffleGameHelpers, RaffleGameHelpers>();

It is the RaffleGameHelpers I am having issue with. It works fine in the server app. Here is the Constructor for it.. all so far so normal (I think?)...

public class RaffleGameHelpers : IRaffleGameHelpers
{
    private readonly IMapper _mapper;
    private readonly IAzureHelpers _azurehelpers;

    public RaffleGameHelpers(IMapper mapper, IAzureHelpers azurehelpers)
    {
        _mapper = mapper;       // Inject AutoMapper
        _azurehelpers = azurehelpers;
    }

In my WebJobs code, the 'Functions.cs' Which contains the main event handlers, is passed a bunch of handlers, because I have been passing some of these down to sub-functions or libs. I think this is probably the wrong way to do this, and they should be likewise directly injected into the libs as required. I'll admit this probably comes from my lack of full understanding in this area.

So at present, my 'Functions' constructor looks like this:

public class Functions
{
    private readonly AppSettings _appSettings;
    private readonly RGDbContext _context;
    private readonly IMapper _mapper;
    private readonly GameHandler _gameHandler;

    // public Functions(AppSettings appSettings, RGDbContext context, GameHandler gameHandler)
    public Functions(AppSettings appSettings, RGDbContext context, IMapper mapper, GameHandler gameHandler)
    {
        _appSettings = appSettings;
        _context = context;
        _mapper = mapper;
        _gameHandler = gameHandler;
    }

Here is the code for my Program.cs

class Program
{
    static async Task Main()
    {
        AppSettings appSettings = new AppSettings();

        var builder = new HostBuilder();

        builder.UseEnvironment("development");

        builder.ConfigureWebJobs(b =>
        {
            b.AddAzureStorageCoreServices();
            b.AddAzureStorageQueues();
            b.AddAzureStorageBlobs();  
            b.AddTimers();

        });

        builder.ConfigureAppConfiguration((context, b) =>
        {
            b.SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddEnvironmentVariables();
        });
        builder.ConfigureServices((context, services) =>
        {
            appSettings.AzureWebJobsStorage = context.Configuration.GetValue<string>("AzureWebJobsStorage");
            appSettings.DataStoreRoot = context.Configuration.GetValue<string>("DataStoreRoot");
            appSettings.DefaultConnection = context.Configuration.GetValue<string>("DefaultConnection");


            // Auto Mapper Configurations
            var mappingConfig = new MapperConfiguration(mc =>
            {
                mc.AddProfile(new MappingProfile());
            });
            IMapper mapper = mappingConfig.CreateMapper();
            services.AddSingleton(mapper);

            //Add Sql
            services.AddDbContext<RGDbContext>(options => options.UseSqlServer(appSettings.DefaultConnection));

            services.AddSingleton<IAzureHelpers>((s) =>
            {
                return new AzureHelpers();
            });


            //Add RaffleGameHelpers
            services.AddScoped<IRaffleGameHelpers, RaffleGameHelpers>();

            //Add the Game Handler
            services.AddScoped<IGameHandler, GameHandler>();  <<*** This one
        });

        builder.ConfigureLogging((context, b) =>
        {
            b.AddConsole();

            // If the key exists in settings, use it to enable Application Insights.
            string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
            if (!string.IsNullOrEmpty(instrumentationKey))
            {
                b.AddApplicationInsightsWebJobs(o => o.InstrumentationKey = instrumentationKey);
            }
        });

        var serviceCollection = new ServiceCollection();

        var program = new Program();

        program.ConfigureServices(serviceCollection, appSettings.DefaultConnection);
        serviceCollection.AddDbContext<RGDbContext>(options => options.UseSqlServer(appSettings.DefaultConnection));

        var host = builder.Build();

        using (host)
        {
            await host.RunAsync();
        }
    }

I have had other tryouts in there commented out but removed for clarity... With the code as here, the Game handler adding doesnt work. With this error message:

Function.PollForGamesToRun[3] Executed 'Functions.PollForGamesToRun' (Failed, Id=62651585-34c7-47b9-a5b1-9ca0da94f71d, Duration=23ms) System.InvalidOperationException: Unable to resolve service for type 'WebJobHandler.BLL.GameHandler' while attempting to activate 'WJMailHandler.Functions'.

I can make the GameHandler instantiate by taking it out of builder.ConfigureServices..

RaffleGameHelpers rghelpers = new RaffleGameHelpers();

at the top of the 'Main' and then adding it in configureservices as follows:

services.AddSingleton(gameHandler);

but when I do, then also instantiating the RaffleGameHelpers lib doesn't in turn instantiate the mapper within it. So I figured I need to get back to basics and do this 'properly' however I seem to be struggling with what that is. So going back to basics... Why doesn't this:

services.AddScoped<IGameHandler, GameHandler>();

work?

CodePudding user response:

Your Functions constructor expects a GameHandler, not an IGameHandler. You should modify your constructor to accept an IGameHandler implementation:

public Functions(AppSettings appSettings, RGDbContext context, IMapper mapper, IGameHandler gameHandler)
{
    _appSettings = appSettings;
    _context = context;
    _mapper = mapper;
    _gameHandler = gameHandler;
}
  • Related