Dependency Injection in ASP.NET Core Worker Service

陌路散爱 提交于 2020-12-31 20:00:17

问题


I'm facing some dependency injection issues in .NET Core Worker Service. Please see the below code in Program.cs file.

public static void Main(string[] args)
{
    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Debug()
        .MinimumLevel.Override("Microsof)t", LogEventLevel.Warning)
        .Enrich.FromLogContext()
        .WriteTo.File(@"C:\MyApp_Log\Log.txt")
        .CreateLogger();

    try
    {
        Log.Information("Starting up the service.");
        CreateHostBuilder(args).Build().Run();
        return;
    }
    catch (Exception ex)
    {
        Log.Fatal(ex, "There was a problem starting the service");
        return;
    }
    finally
    {
        Log.CloseAndFlush();
    }

}
public static IHostBuilder CreateHostBuilder(string[] args)
{

    return Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureServices((hostContext, services) =>
        {

            services.AddScoped<IMyAppCoreService, MyAppCoreService>();

            services.AddDbContext<MyAppCSContext>(options => options.UseSqlServer("Data Source=xx.xxx.xx.xxx;Database=Mydb;User ID = sa;Password=mypassword"));


            services.AddHostedService<Worker>();
        })
        .UseSerilog();
}

And please see below code for Worker.cs file

private readonly ILogger<Worker> _logger;
private readonly IMyAppCoreService _CoreService;

public Worker(ILogger<Worker> logger, IMyAppCoreService CoreService)
{
    _logger = logger;
    _CoreService = CoreService;
}
public override Task StartAsync(CancellationToken cancellationToken)
{
    _logger.LogInformation("The MyApp_CoreService has been Started...");
    return base.StartAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
    _logger.LogInformation("The MyApp_CoreService has been stopped...");
    return base.StopAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
        _CoreService.CheckAndProcessResult();
        await Task.Delay(1000, stoppingToken);
    }
}

When I run the above query, I got the below query.

Error while validating the service descriptor

ServiceType: MyApp.CS.Business.Facade.IMyAppCoreService Lifetime: Scoped ImplementationType: MyApp.CS.Business.Services.MyAppCoreService': Unable to resolve service for type 'MyApp.CS.Data.Facade.ICommonRepository' while attempting to activate 'MyApp.CS.Business.Services.MyAppCoreService'.

Can you please tell me where I was done wrong?

EDIT: After i register all the interface with its class. then i got new error as follows.

Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: MyApp_CoreService.Worker': Cannot consume scoped service 'MyApp.CS.Business.Facade.IMyAppCoreService' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.


回答1:


You injected the Serivce IMyAppCoreService as Scoped. Scoped Services can only be resolved by a ScopedServiceProvider.

My first guess is that you didn't mean to - you meant to inject your service as Singleton:

services.AddSingleton<IMyAppCoreService, MyAppCoreService>();

This however might not work since you are using EF Core which injects its Context-like classes as scoped. You have two options:

  1. Have EF Core inject its context class as transient. I would not suggest this as you will be responsible for disposing it. If you want to go through with it, this is how to do it (Startup.cs / Program.cs):
services.AddDbContext<YourContext>(opts => { ...config...}, ServiceLifetime.Transient);
  1. Create a scope manually:

Have an IServiceProvider property called ServiceProvider injected into Worker instead of your service.

in ExecuteAsync-Loop:

_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
using (var scope = ServiceProvider.CreateScope())
{
  scope.ServiceProvider.GetRequiredService<IMyAppCoreService>().CheckAndProcessResult();
}
await Task.Delay(1000, stoppingToken);

This will neatly dispose every Loop's EFCore-Data Context Object and in my opinion is the cleanest option.




回答2:


Dependency injection:

 services.AddScoped<IMyAppCoreService, MyAppCoreService>();

 services.AddDbContext<MyAppCSContext>(options => options.UseSqlServer("Data Source=xx.xxx.xx.xxx;Database=Mydb;User ID = sa;Password=mypassword"), ServiceLifetime.Scoped);

Worker class:

public class Worker : BackgroundService
{
    IServiceProvider _serviceProvider;

    public Worker(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        using (var scope = _serviceProvider.CreateScope())
        {
            scope.ServiceProvider.GetRequiredService<IMyAppCoreService>().CheckAndProcessResult();
        }

        await Task.Delay(1000, stoppingToken);
    }
}

Source https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.0&tabs=visual-studio#consuming-a-scoped-service-in-a-background-task



来源:https://stackoverflow.com/questions/62066501/dependency-injection-in-asp-net-core-worker-service

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!