0
votes

In my application, I have an API and a worker, that both need to use my database. I have a repository layer, that is accessed by both of these. I use DI in my application, and I inject my dbContext in my repositories.

While I expect my repositories to use a new dbContext on every request, the instance seems to always be the same.

Since I have a background worker, which is a singleton, I was not able to use the default scoped lifetime of my dbContext. Therefore I have added my dbContext as transient in both my worker and my API. I have added an instanceId to my dbContext, which is set in my constructor.

Constructor of dbcontext:

 public CatAPIDbContext()
    {
        InstanceId = Guid.NewGuid();
        Database.EnsureCreated();
    }

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>();
        });

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<>));
}

GenericRepo example:

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);
}
}

Repo that I use and does not get latest state example:

 public class FeedingProfileRepository : 
GenericRepository<FeedingProfile>, IFeedingProfileRepository
{
    public FeedingProfileRepository(CatAPIDbContext dbContext) : 
base(dbContext)
    {
    }

    public FeedingProfile GetCurrentFeedingProfile()
    {
        var profile = _dbContext.FeedingProfiles
            .Include(x => x.TimesToFeedOn)
            .Where(x => x.CurrentlyActive == true).FirstOrDefault();


        if (profile == null)
        {
            return null;
        }
        if (profile.TimesToFeedOn != null)
        {
            profile.TimesToFeedOn = profile.TimesToFeedOn.OrderByDescending(x => x.Time).ToList();
        }
        return profile;

    }
}

When thw orker calls FeedingProfileRepository.GetCurrentFeedingProfile() I check the instanceId of the dbContext, and it is always the same during the entire lifetime of my application. As a result, the data I retrieve from the dbContext is outdated, and does not match the current state of the database, since the dbContext never gets disposed. Am I doing something wrong?

1

1 Answers

0
votes

As I see in your code you've made dbContext Transient and it means that it is created a new instance every time they are injected or requested:

 services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Transient);

if you want to have a single instance for all of the requests, make it singleton try to change your code like below:

services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Singleton);

But if you want to have a single instance for every request to application try to use scoped lifetime:

 services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Scoped);