The DbContext returns irrelevant data from the Oracle database. Getting the database context is implemented through IServiceScopeFactory, because the method that adds the DbContext to the DI container is Scoped, and my part uses IHostedService, which requires creating an area (Scope).
protected SomeCtor(...)
{
IServiceScope scope = scopeFactory.CreateScope();
DbContext = scope.ServiceProvider.GetRequiredService<TContext>();
}
I then retrieve the data in this way:
protected override IAsyncEnumerable<TItem> GetItemsFromDBAsync(CancellationToken cancellationToken)
{
return DbContext.Set<TItem>().Where(...).AsAsyncEnumerable();
}
My DbContext is set in a standard way by inheriting from Microsoft.EntityFrameworkCore.DbContext
public class SomeDbContext : DbContext
{
(...)
public SomeDbContext (DbContextOptions<SomeDbContext > options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.HasDefaultSchema("SchemaName");
builder.Entity<SomeItem1>(x => x
.ToTable("SomeTable1")
);
builder.Entity<SomeItem2>(x => x
.ToTable("SomeTable2")
);
base.OnModelCreating(builder);
}
}
And often ( not every time) I get data from a table that is no longer in the database. It feels like the context is not updated. Correct data starts loading after several program restarts.
No one else works with this table.
DbContext = scope.ServiceProvider.GetRequiredService<TContext>();
I would recommend against doing that in the service. It seems that you resolve the SomeCtor
in your IHostedService
which makes lifetime of the DbContext
match the lifetime of the hosted service itself. DbContext
is designed to be lightweight so I highly recommend to switch to "per iteration" approach, i.e. let the IHostedService
create short-lived scopes and resolve needed stuff inside the scope:
class MyHostedService : IHostedService
{
private readonly IServiceScopeFactory _scopeFactory;
public MyHostedService(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// TODO: manage cancellation/stoppage
while (true)
{
await using var scope = _scopeFactory.CreateAsyncScope();
// SomeService - usual scoped service using DbContext, etc. ...
var service scope.ServiceProvider.GetRequiredService<SomeService>();
await service.ProcessIterationAsync();
}
}
// ...
}
The problem you are facing is highly likely related to the fact that you are not disposing/recreating/clearing context. EF Core is using tracking to manage changes and if there is some stale information in change tracker it can lead to reading stale data from it without hitting database (or even hitting it but still using some parts of stale data, also I have performance issues related to change tracker in such scenarios at least in earlier versions of EF Core). At least call Clear
on the change tracker - DbContext.ChangeTracker.Clear();
Also check out consuming a scoped service in a background task parts of the docs.