Blazor Server: Cannot access a disposed object.

Charles Perry 0 Reputation points
2023-02-06T22:28:21.37+00:00

In a Blazor Server application, I am trying to query the database (via EF) on a timer and load data if there is an update. I am using a new dbcontext object each time so I am confused why it is trying to access something that is disposed of.

Error Msg:

An exception of type 'System.ObjectDisposedException' occurred in System.Private.CoreLib.dll but was not handled in user code

Cannot access a disposed object.

@code {

    [Parameter] public string GameID { get; set; }
    private int GameIDInt;
    private System.Threading.Timer? timer;
    private int CurQues = 0;
    private Question QuestionInfo;
    private string CategoryName;
    private string CorrectAnswer;
    private List<Clue> AllAnswers;

    protected override async Task OnInitializedAsync()
    {
        GameIDInt = Int32.Parse(GameID);
        timer = new System.Threading.Timer(async (object? stateInfo) =>
        {
            await CheckForNewAnswer();
        }, new System.Threading.AutoResetEvent(false), 3000, 3000);
    }

    async Task CheckForNewAnswer()
    {
        using (TriviaContext context = await GetNewDbContextAsync())
        {
            // Errors on the following line
            Game ThisGame = await context.Games.AsNoTracking().SingleAsync(g => g.ID == GameIDInt);
            int QuesBeingViewed = ThisGame.CurrentQuestion;
            if (QuesBeingViewed != CurQues)
            {
                QuestionInfo = await context.Questions.AsNoTracking().SingleAsync(q => q.ID == QuesBeingViewed);
                Category cat = await context.Categories.AsNoTracking().SingleAsync(c => c.ID == QuestionInfo.CategoryID);
                CategoryName = cat.CategoryName;
                if (QuestionInfo.TypeOfQuestion == "MULTIC"){
                    CorrectAnswer = context.PossibleAnswers.AsNoTracking().Single(ca => ca.QuestionID == QuesBeingViewed && ca.Correct).Chunk;
                }
                else if (QuestionInfo.TypeOfQuestion == "NAMALL")
                {
                    CorrectAnswer = "";
                    AllAnswers = context.Clues.AsNoTracking().Where(c => c.QuestionID == QuesBeingViewed).OrderBy(o => o.Seq).ToList();
                }
                else
                {
                    CorrectAnswer = QuestionInfo.Answer;
                }
                CurQues = QuesBeingViewed;
            }
        }
        
    }

}


HERE IS THE CODE FROM AppBaseComponent.razor AS IT MAY BE RELEVANT

private TriviaContext _dbContext;

    protected TriviaContext DbContext
    {
        get
        {
            return _dbContext ??= GetNewDbContext();
        }
    }

    //Reference: https://docs.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-6.0
    [Inject]
    private IDbContextFactory<TriviaContext> DbContextFactory { get; set; }

    protected TriviaContext GetNewDbContext()
    {
        var dbContext = DbContextFactory.CreateDbContext();
        return dbContext;
    }

    protected async Task<TriviaContext> GetNewDbContextAsync()
    {
        var dbContext = await DbContextFactory.CreateDbContextAsync();
        return dbContext;
    }
.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,362 questions
Blazor
Blazor
A free and open-source web framework that enables developers to create web apps using C# and HTML being developed by Microsoft.
1,383 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Bruce (SqlWork.com) 55,366 Reputation points
    2023-02-07T20:44:41.1366667+00:00

    my guess is that the DbContextFactory was been disposed.

    0 comments No comments

  2. Bruce (SqlWork.com) 55,366 Reputation points
    2023-03-01T17:50:34.9366667+00:00

    the factory is disposed when the component render is done. you should pass a dbcontect by closure.

    
      protected override async Task OnInitializedAsync()
        {
            GameIDInt = Int32.Parse(GameID);
            var context = await GetNewDbContextAsync()
            timer = new System.Threading.Timer(async (object? stateInfo) =>
            {
                await CheckForNewAnswer(context); 
            }, new System.Threading.AutoResetEvent(false), 3000, 3000);
        }
    
        async Task CheckForNewAnswer(TriviaContext context)
        {
             ... 
        } 
    
    

    you should write your own event handler that will dispose the context when timer is done. another option is to use a singleton factory, which you could pass by closure (but you would need to expose)

    0 comments No comments