I'm writing an ASP.NET Core API in which the dependency injection container provides me with instances of my DbContext
class. I set the service lifetime of the DbContext
to transient (which means that the container will create a new instance for every class and every request).
builder.Services.AddDbContext<ComparisonInfoContext>(dbContextOptions =>
dbContextOptions.UseSqlServer("Connection string"), ServiceLifetime.Transient, ServiceLifetime.Transient);
Here is my DbContext
class:
public class ComparisonInfoContext : DbContext
{
public DbSet<ComparisonInfo> Comparisons { get; set; } = null!;
public ComparisonInfoContext(DbContextOptions<ComparisonInfoContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ComparisonInfo>().OwnsMany(c => c.DiscrepancyInfos);
}
}
I also have a BackgroundService
class called JobChecker
which I added by calling:
builder.Services.AddHostedService<JobChecker>();
In JobChecker
, I have a function that runs every minute and the first thing that function does is call context.Comparisons.ToListAsync()
. The first time the function runs everything works fine, but the second time, this call returns outdated values for some reason (The values it returns used to be in the database but were changed prior to this call being made).
What I think is happening is the following:
JobChecker
(the BackgroundService class) receives it's instance ofDbContext
at the very start of the program.- The function inside
JobChecker
callscontext.Comparisons.ToListAsync()
and gets the correct values from the database (no problems here). - Some controller somewhere changes the values in the database using it's own instance of the
DbContext
. - The function inside
JobChecker
runs and callscontext.Comparisons.ToListAsync()
. It sees that it is already tracking instances with the sameid
which came from the previous call (step 2). These instances were not effected by the changes made by the controller in step 3. It does not materialize the values it got from the database, instead it returns the instances that already exist. This is explained more here:CodePudding user response:
Your DB Context might be transient, but the
BackgroundService
is not. In effect you only have a single instance of the context in theBackgroundService
.I would look at injecting in an DB Context Factory:
services.AddDbContextFactory<ApplicationDbContext>(...)
Then inject one into your background service:
public class MyBackgroundService(IDbContextFactory<ApplicationDbContext> contextFactory)
Then every iteration of your loop in the background service (it sounds like it is running on a timer), you can use this to instantiate a new context.
using (var context = _contextFactory.CreateDbContext()) { // ... }