0

I stumbled upon this error while running my application.

System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: testing.CacheUpdater': Unable to resolve service for type 'testing.CacheMonitorOptions' while attempting to activate 'testing.CacheUpdater'.

App explanation I am making an application where I periodically(every 10secs) update the MemoryCache with values I fetch from my database.

For this I am using 3 classes, CacheMonitor (which is responsible for the update/override of cache), StudentsContext (which is responsible for fetching the data from the database) and CacheUpdater which is a background service that calls the Update method within the CacheMonitor class.

I have injected them into my DI container like this:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMemoryCache();
    services.AddHostedService<CacheUpdater>();
    services.AddDbContext<StudentsContext>(options =>
    {
        options.UseSqlServer(Configuration["Database:ConnectionString"]);
    });

    services.Configure<CacheMonitorOptions>(Configuration.GetSection("CacheUpdater"));

    services.AddTransient<ICacheMonitor, CacheMonitor>();

    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "testing", Version = "v1" });
    });
}

CacheMonitor.cs

public class CacheMonitor : ICacheMonitor
{
    private readonly IMemoryCache _cache;
    private readonly ILogger<CacheMonitor> _logger;
    private readonly StudentContext _databaseContext;

    public CacheMonitor(
        IMemoryCache cache,
        IOptions<CacheMonitor> options,
        StudentContext context,
        ILogger<CacheMonitor> logger)
    {
        this._cache = cache;
        this._databaseContext = context;
        this._logger = logger;
    }

    public void UpdateCache()
    {
       //updates cache
    }
}

CacheUpdater.cs

public class CacheUpdater{
    private readonly ICacheMonitor _cacheMonitor;
    private readonly CacheMonitorOptions _cacheMonitorOptions;
    private readonly ILogger<CacheUpdater> _logger;

    public CacheUpdater(
        ICacheMonitor cacheMonitor,
        CacheMonitorOptions cacheMonitorOptions,
        ILogger<CacheUpdater> logger)
    {
        _cacheMonitor = cacheMonitor;
        _cacheMonitorOptions = cacheMonitorOptions;
        _logger = logger;
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation($"trying to update cache");
        _cacheMonitor.UpdateCache();
        Thread.Sleep(_cacheMonitorOptions.Interval);

        return Task.CompletedTask;
    }
}

I know that it has to do something with the Lifetime of the services but I am not sure how to fix it.

Steven
  • 166,672
  • 24
  • 332
  • 435
schweppes0x
  • 182
  • 1
  • 13
  • It's important to check any exceptions to see if the InnerException or InnerExceptions property is populated. If so, include those details in your question. They're usually populated for an AggregateException. – mason Oct 27 '21 at 12:44

1 Answers1

2

Change CacheUpdater ctor to accept IOptions<CacheMonitorOptions> instead of options (with corresponding changes to other code):

 public CacheUpdater(
    ICacheMonitor cacheMonitor,
    IOptions<CacheMonitorOptions> cacheMonitorOptions,
    ILogger<CacheUpdater> logger
    )
    {
        ...
    }

Also check out the docs.

UPD

Addressing question from the comments - if you don't want to use pattern from timed background tasks in docs you can do something along this lines (not tested):

public class CacheUpdater
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly CacheMonitorOptions _cacheMonitorOptions;
private readonly ILogger<CacheUpdater> _logger;

public CacheUpdater(
    IServiceScopeFactory scopeFactory,
    CacheMonitorOptions cacheMonitorOptions,
    ILogger<CacheUpdater> logger
    )
    {
        _scopeFactory = scopeFactory;
        _cacheMonitorOptions = cacheMonitorOptions;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while(!stoppingToken.IsCancellationRequested) // !
        {
            _logger.LogInformation($"trying to update cache");
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                 var cacheMonitor = scope.ServiceProvider.GetService<ICacheMonitor>(); 
                cacheMonitor.UpdateCache();
                await Task.Delay(_cacheMonitorOptions.Interval, stoppingToken); // DO NOT USE THREAD SLEEP HERE!
            }
        }
    }
}
Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Now i get another error: System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: testing.CacheUpdater': – schweppes0x Oct 27 '21 at 12:41
  • Cannot consume scoped service 'testing.Models.StudentsContext' from singleton 'testing.ICacheMonitor'.) (Error while validating the service descriptor 'ServiceType: testing.ICacheMonitor Lifetime: Singleton ImplementationType: testing.CacheMonitor': Cannot consume scoped service 'testing.Models.StudentContext' from singleton 'testing.ICacheMonitor'.)' – schweppes0x Oct 27 '21 at 12:42
  • 1
    @schweppes0x it is totally other error. In short you need to inject `IServiceScopeFactory` not `StudentContext`, create when context is needed and resolve context from scope, like [here](https://stackoverflow.com/a/51618983/2501279). – Guru Stron Oct 27 '21 at 12:44
  • @schweppes0x see the update. Note that I have not tested the code so probably it can require some modifications. – Guru Stron Oct 27 '21 at 13:03
  • It seems to work now, but the periodical update does not work. For some reason the backgroundservice is not working after the first time. – schweppes0x Oct 27 '21 at 13:07
  • @schweppes0x I will need [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) to see what is the problem. "does not work" is not very helpful to diagnose your problem. – Guru Stron Oct 27 '21 at 13:20