the past days I have been struggling with injecting a DbContext in MY background worker. On the one hand, I want to inject the dbContext in my backgorund worker, but on the other hand I also want to use it in my API.
The injecting in my API seems to work fine, but since my worker is a singleton, I can not follow the standard lifetime of scoped for my dbcontext, and I have to add it as transient.
I have already tried to create a unit of work, in which I can refresh the context myself in my worker, effectively creating some kind of scoped service. I would refresh the context every time the worker went through his loop once again. This worked, and the application was working as I wanted, but I was no longer able to properly test, since I would create a new DbContext myself in the code. I feel like there must be a better way for to handle this.
My project structure looks like the following:
API => contains controlers + models I use for post requests. The API project needs to use my database, to get and post data. It uses the repositories for this
Core (class library) => contains some core models
Domain(class library) => Contains my domain models + repositories. All database work goes through here
Worker => Contains some logic. The worker needs to use my database, to get and post data. It uses the repositories for this
Services (class library) => Some services that contain some logic. The worker uses my repositories to get to the database
Tests => Tests for all code. I want to be able to to integrationTesting as well here.
I currently inject all repositories and services in both my API and worker:
Worker configureservices:
  public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Transient);
                services.AddTransient(typeof(IFeedingProfileRepository), typeof(FeedingProfileRepository));
                services.AddTransient(typeof(IFeedingTimesRepository), typeof(FeedingTimesRepository));
                services.AddTransient(typeof(IFeedHistoryRepository), typeof(FeedHistoryRepository));
                services.AddTransient(typeof(IMotorController), typeof(MotorController));
                services.AddTransient(typeof(IFoodDispenser), typeof(FoodDispenser));
                services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
                services.AddTransient(typeof(IFeedingTimeChecker), typeof(FeedingTimeChecker));
                services.AddHostedService<Worker>();
            });
(EDIT)Worker code:
public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    public IFeedingTimeChecker _feedingTimeChecker { get; }
    public Worker(ILogger<Worker> logger, IFeedingTimeChecker feedingTimeChecker)
    {
        _logger = logger;
        _feedingTimeChecker = feedingTimeChecker;
    }
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                _feedingTimeChecker.ResetFeedingTimesGivenIfNeeded();
                _feedingTimeChecker.FeedIfNeeded();
            }
            catch(Exception ex)
            {
                _logger.LogError(ex.ToString());
            }
            await Task.Delay(10000, stoppingToken);
        }
    }
}
(EDIT)FeedingTimeChecker (called by worker)
private FeedingProfile _currentProfile { get; set; }
    public DateTime lastResetDataFeedingTimes;
    public DateTime lastProfileRefresh;
    private readonly ILogger<FeedingTimeChecker> _logger;
    private IFeedingProfileRepository _feedingProfileRepository { get; set; }
    private IFeedingTimesRepository _feedingTimesRepository { get; set; }
    private IFoodDispenser _foodDispenser { get; }
    public FeedingTimeChecker(IFeedingProfileRepository feedingProfileRepository, IFeedingTimesRepository feedingTimesRepository,IFoodDispenser foodDispenser, ILogger<FeedingTimeChecker> logger)
    {
        lastResetDataFeedingTimes = DateTime.MinValue.Date;
        lastProfileRefresh = DateTime.MinValue.Date;
        _foodDispenser = foodDispenser;
        _logger = logger;
        _feedingTimesRepository = feedingTimesRepository;
        _feedingProfileRepository = feedingProfileRepository;
    }        
    public void UpdateCurrentProfile()
    {
        if(Time.GetDateTimeNow - TimeSpan.FromSeconds(5) > lastProfileRefresh)
        {
            _logger.LogInformation("Refreshing current profile");
            _currentProfile = _feedingProfileRepository.GetCurrentFeedingProfile();
            lastProfileRefresh = Time.GetDateTimeNow;
        }
    }
API configureServices:
 public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvcCore().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
        services.AddDbContext<CatAPIDbContext>();
        services.AddTransient(typeof(IFeedingProfileRepository), typeof(FeedingProfileRepository));
        services.AddTransient(typeof(IFeedingTimesRepository), typeof(FeedingTimesRepository));
        services.AddTransient(typeof(IFeedHistoryRepository), typeof(FeedHistoryRepository));
        services.AddTransient(typeof(IMotorController), typeof(MotorController));
        services.AddTransient(typeof(IFoodDispenser), typeof(FoodDispenser));
        services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
    }
in my repositories I use the dbContext like the following:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
    public CatAPIDbContext _dbContext { get; set; }
    public GenericRepository(CatAPIDbContext dbContext)
    {
        _dbContext = dbContext;
    }
    public T GetById(object id)
    {
        return _dbContext.Set<T>().Find(id);
    }
}
The result I would expect, is for my worker and API to behave correctly, always getting the lastest data and disposing of the dbContext on every single request, since I use a transient lifetime for my dbContext.
However, in my worker, I always get the following error: The instance of entity type 'FeedingTime' cannot be tracked because another instance another instance of this type with the same key is already being tracked.
This error occurs when I try to set a column in the FeedingTime table. A feedingProfile has 0-many feedingTimes, and the feedingProfile constantly retrieved.
Any solution where I can keep a testable clean codebase, but yet not run into this problem would be very welcome.
Thanks in advance
