简体   繁体   中英

Dependency injection in a class not working on a worker service

Is there a way to DI something into a class?

For example,

I have a .net 6 worker service that looks like the following:

Startup:

IConfiguration builder =
    new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json", false, true)
    .AddEnvironmentVariables()
    .Build();

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
        services.AddSingleton(mapper);

        services.AddScoped<MpwContext>(
            serviceProvider =>
            {
                return new MyDBContext(builder.GetSection("MyDBConnectionString").Value.ToString());
            });
    }

Worker.cs:

      public Worker(ILogger<Worker> logger, IConfiguration configuration, IMapper mapper)
        {
            //All the constructor stuff here
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var partService = new PartService();

            await partService.DoSomething();
        }

PartService:

       public PartService(IMapper mapper, MyDbContext myDbContext)
        {
            _mapper = mapper;
            _myDbContext = myDbContext;
        }

        public async Task DoSomething()
        {
        }

The issue I am facing is where I say: var partService = new PartService(); It is telling me that I am missing all the paramters in the constructor.

There is no argument given that corresponds to the required field MyDbContext and mapper....

I know with mvc the constructors pick up on the DI stuff on its own, how can I do that in this case?

The Databases are scoped so I cannot consume them directly in the worker, so I need to DI them in to each respective service. But its asking for them on the object creation.

You've almost got it.

It's normal for background services to be singletons (and thus only allow singleton dependencies). By default, there's no DI scope within ExecuteAsync , but you can create your own :

private readonly IServiceProvider _serviceProvider;

public Worker(IServiceProvider serviceProvider, ILogger<Worker> logger, IConfiguration configuration, IMapper mapper)
{
  _serviceProvider = serviceProvider;
  ...
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
  using var scope = _serviceProvider.CreateScope();

  // Not working yet:
  var partService = new PartService();
  await partService.DoSomething();
}

The other thing you need to know is that the .NET DI is rather simple-minded. The common pattern is to explicitly register every single type you need, including types that only have a single constructor:

services.AddHostedService<Worker>();
services.AddSingleton(mapper);

services.AddScoped<MpwContext>(
    serviceProvider =>
    {
      return new MyDBContext(builder.GetSection("MyDBConnectionString").Value.ToString());
});
services.AddScoped<PartService>();

Now that you've defined PartService as a scoped service, you can ask the DI to create an instance for you:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
  using var scope = _serviceProvider.CreateScope();

  var partService = scope.ServiceProvider.GetRequiredService<PartService>();
  await partService.DoSomething();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM