7

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'.

3
  • I believe you need to register ICommonRepository like services.AddScoped<ICommonRepository, ClassImplementingICommonRepository> Commented May 28, 2020 at 14:04
  • After i posted this content i tried to register ICommonRepository like services.AddScoped<ICommonRepository, CommonRepository> . But still having some error. Now it showing. Cannot consume scoped service 'MyApp.CS.Business.Facade.IMyAppCoreService' from singleton 'Microsoft.Extensions.Hosting.IHostedService'. Commented May 28, 2020 at 14:12
  • Now I register every interface with corresponding class... And the now i got the new error.. 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'. Commented May 28, 2020 at 14:39

2 Answers 2

15

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~ you might get surprised by receiving different instances. 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.

Edit (2024): I have been made aware that this contains incorrect information. On https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines it is documented that you should not dispose Services resolved from DI in dotnet.

Sign up to request clarification or add additional context in comments.

2 Comments

When i write using (var scope = ServiceProvider.CreateScope()) the CreateScope is missing. I am using .net Core 3.1. I have used using Microsoft.Extensions.DependencyInjection . Do we need to import some other libraries. ?
@DileepKumar Make sure to call CreateScope on the ServiceProvider instance and not the class. The second answer is clearer on this.
6

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://learn.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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.