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.
CodePudding user response:
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!
}
}
}
}