Blazor Server App and IDbContextFactory not disposing

Viewed 575

I have a blazor server application that needs to indirectly connect to a EF core DB context.

None of the blazor components will directly inject an instance of the dbcontext. I am using mediator which will handle all business operations.

The documentation that I have seen so far recommends using IDbContextFactory. I gave it a try but I am not seeing the DbContext created by the factory being disposed. The services that inject IDbContext are not disposed on page changes nor at any other time.

public class QueryHandler : IQueryHandler<Query, Entity>, IDisposable
{
    private readonly DbContext dbContext;

    public QueryHandler(IDbContextFactory factory)
    {
        dbContext = factory.CreateDbContext();
    }

    public Task Handle(Query query)
    {
       /// do whatever needs to be done.
    }
  
    public void Dispose()
    {
        dbContext.Dispose(); // <-- Dispose never gets called.
    }
}

Am I missing something?

2 Answers

The purpose of using a DbContextFactory is to have a DbContext per method. Exactly because Blazor doesn't offer useful Scopes to handle this.

public class QueryHandler : IQueryHandler<Query, Entity> //, IDisposable
{
    ...

    public QueryHandler(IDbContextFactory factory)
    {
        _factory = factory;
    }

    public Task Handle(Query query)
    {
       using var dbContext = _factory.CreateDbContext();

       /// do whatever needs to be done.
    }
  
    //public void Dispose() { }

}

This way the DI container and Factory only manage the configuration of the DbContext. Lifecycle management of the DbContext is manual. The Factory is a simple Transient object, is owns no resources.

Manual management usually is with a using statement or using declaration but Blazor also offers an OwningComponentBase. I don't see it being used much.

In Server the DI container exists for the lifetime of the Hub Session and in WASM the lifetime of the Application. Any service objects created within the container, whether Scoped or Transient, implementing IDisposable, are not Disposed until the DI container itself is destroyed. You don't make clear the scope of QueryHandler, but if it's transient that's bad news. You will keep creating new DBContexts without the old ones being disposed.

The purpose of the DbContextFactory is to create unit of work DbContext instances that are used and then quickly disposed correctly. You need to take this approach because DB access will almost certainly be asynchronous. Use a single context and you will quickly hit the situation where you are awaiting one query to complete while trying to use the same context in another operation.

Henk's answer shows you how to use and consume factory created contexts.

Related