Home > Blockchain >  System.AggregateException: 'Some services are not able to be constructed
System.AggregateException: 'Some services are not able to be constructed

Time:10-28

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!
            }
        }
    }
}
  • Related