my guess is that the DbContextFactory was been disposed.
Blazor Server: Cannot access a disposed object.
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;
}
2 answers
Sort by: Most helpful
-
-
Bruce (SqlWork.com) 66,621 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)