Home > front end >  Blazor Server System.ObjectDisposedException issues when updating pages
Blazor Server System.ObjectDisposedException issues when updating pages

Time:10-06

Added the process to update the data every second in Blazor Server.

This works fine, but when I press refresh page (F5) in my browser I get the following error:

System.ObjectDisposedException: 'Cannot process pending renders after the renderer has been disposed.
ObjectDisposed_ObjectName_Name'

The target code is here

@code {
  private List<Models.Recipe> recipes { get; set; }
  private List<Models.NowOrder> nowOrders { get; set; }
  private List<Models.PlanOrder> planOrders { get; set; }
  System.Threading.Timer _timer;

  protected override void OnInitialized()
  {
    using (var dbContext = DbFactory.CreateDbContext())
    {
        this.recipes = dbContext.Recipes.ToList();
        this.planOrders = dbContext.PlanOrders.ToList();
        this.nowOrders = dbContext.NowOrders.ToList();
    }

    _timer = new System.Threading.Timer(async (_) =>
    {
        Time = DateTime.Now.ToString();
        databaseValue = await TimerProcessGetValue();
        await InvokeAsync(StateHasChanged);    
    }, null, 0, 1000);
  }

  public void Dispose() 
  {
    _timer?.Dispose();
  }

  public async Task<int?> TimerProcessGetValue()
  {
      int? timerProcessValue;
      using (var dbContext = DbFactory.CreateDbContext())
      {
         timerProcessValue = (await dbContext.TestTable.SingleAsync(x => x.Id == 1)).TestValue;
      }
      return timerProcessValue;
  }
}

When refreshing the page "await InvokeAsync(StateHasChanged);"

If you comment out the following part, you will not get an error even if you press the F5 key, so I think that my handling of asynchronous processing is wrong, but I am troubled because I can not find the same case even if I search for the error I am.

What do you think is the cause of this? Thank you for your cooperation.

_timer = new System.Threading.Timer(async (_) =>
{
    Time = DateTime.Now.ToString();
    databaseValue = await TimerProcessGetValue();
    await InvokeAsync(StateHasChanged);    
}, null, 0, 1000);

Change to verification code

        _timer = new System.Threading.Timer(async (_) =>
    {
        Time = DateTime.Now.ToString();
        //databaseValue = await TimerProcessGetValue();
        await Task.Delay(500); //Added verification code.
        await InvokeAsync(StateHasChanged);    
    }, null, 0, 1000);
  }

CodePudding user response:

https://docs.microsoft.com/en-us/dotnet/api/system.threading.timer?view=net-5.0

When a timer is no longer needed, use the Dispose method to free the resources held by the timer. Note that callbacks can occur after the Dispose() method overload has been called, because the timer queues callbacks for execution by thread pool threads. You can use the Dispose(WaitHandle) method overload to wait until all callbacks have completed.

This is why await InvokeAsync(StateHasChanged); is being called after the component is disposed.

CodePudding user response:

To expand on @Brianparker's answer:

replace @implements IDisposable with @implements IAsyncDisposable

and then replace the Dispose() method with

public ValueTask DisposeAsync()
{
    return _timer?.DisposeAsync() ?? ValueTask.CompletedTask;
}
  • Related