Search code examples
c#entity-frameworktimer

Why is my fetch data looking at old database data instead of updated data of database?


I have a timer which will elapse every 10 seconds. After the timer has elapsed, it will call my service and then fetch data from my service and assign it to my radzen table and then StateHasChanged will re-render the page and update the changes.

@using System.Timers;

Timer _timer;
protected override async Task OnInitializedAsync()
{
      StartTimer();
}

private void StartTimer()
    {
        _timer = new System.Timers.Timer();
        _timer.Interval = 10000; //every 10 seconds
        _timer.Elapsed += DashboardTimerElapsed;

        _timer.Start();

    }

private async void DashboardTimerElapsed(Object source, ElapsedEventArgs e)
    {
        await FetchData();

    }

private async Task FetchData()
    {
        dashboardData = await getDataService.GetDataAsync();
        await InvokeAsync(StateHasChanged);
    }

The timer elapsed works and everything, the only problem I am getting is if my application is running, I make a change to one of the data in the database for example: Change value name "shazam" to "shazamm". On elapsed my new data should pull down "shazamm" but when I look at dashbardData, I can see that it is not pulling down "shazamm" but instead pulling down "shazam" which I assume is the old instance of the dbcontext. (Btw, my dbcontext is scoped) -

services.AddDbContext<DbContext>(options => {
                options.UseSqlServer(Configuration.GetConnectionString("DbContextURL"));
                }, ServiceLifetime.Scoped);

Can anyone see where I am going wrong and help me please? THank you!


Solution

  • Timer events will trigger in the same scope as the instance of the timer. Options include:

    a) Use LifetimeScope.Transient, though that will probably conflict with other services and such where it would benefit from an instance shared across the Scope, especially if entities are being passed around.

    b) Scoping a DbContext instance within your timer rather than relying on the shared Service using an injected DbContext.

    c) Use AsNoTracking() for your timer read. This will ensure the entity returned is read from the DB. (Verified with EF6, should be consistent in EF Core) This may mean introducing something like a defaulted parameter in your Service to indicate whether to append AsNoTracking() to the query(ies) to ensure a fresh read.

    d) Base your queries on projection rather than returning entity instances. For example when you write your queries to use .Select() or .ProjectTo() (Automapper) these will automatically return the current database state, avoiding stale data that might be in tracked instances.