2

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?

  • This has nothing to do with EF Core and `IDbContextFactory`. The problem as I understand is that your `QueryHandler` is not disposed. – Ivan Stoev Dec 13 '21 at 15:41
  • I am using Microsoft.Extensions.DependencyInjection. It is my understanding that it would automatically dispose of services depending on their registered scope. – Torrey Garland Dec 13 '21 at 15:47
  • What is the scope though? In Blazor Server, it's the client circuit - essentially the entire session – Panagiotis Kanavos Dec 13 '21 at 15:50
  • There is a new base component that helps to control the lifetime of a service provider scope. `OwningComponentBase` & `OwningComponentBase` https://source.dot.net/#Microsoft.AspNetCore.Components/OwningComponentBase.cs – Brian Parker Dec 13 '21 at 17:07
  • The factory is not disposable, the objects it creates are. But the objects it creates are not registered in the DI container and it's your responsibility to dispose them. Pretty standard behavior for factory (same as using `new` operator with disposable object). – Ivan Stoev Dec 13 '21 at 17:52

2 Answers2

2

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.

H H
  • 263,252
  • 30
  • 330
  • 514
  • 1
    Just because they don't work the same way MVC does doesn't mean they aren't useful. In Blazor WASM the scope is the page/component. In Blazor Server it's the circuit. Blazor is an SPA, so the "scope" is far wider than a single request – Panagiotis Kanavos Dec 13 '21 at 15:52
  • 1
    @enet yes, the `using` directive is supposed to work here – lenniep Dec 14 '21 at 06:05
  • @lenniep, are you sure about that ? Can you provide a link to code where it is used like that. Perhaps from the docs... – enet Dec 14 '21 at 06:34
0

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.

MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31
  • `"In Server the DI container exists for the lifetime of the Hub Session and in WASM the lifetime of the Application. "` : True. `Any service objects created within the container, whether Scoped or Transient, are not Disposed until the DI container itself is destroyed.` : False. Scoped and transient objects created by the DI container will get disposed, if they implement the IDisposable interface. See this: https://github.com/aspnet/DependencyInjection/blob/70d289e12b98b5d84555e4b341b986c7850e40a3/src/Microsoft.Extensions.DependencyInjection/ServiceProvider.cs#L157-L186 – enet Dec 14 '21 at 06:19
  • @HenkHolterman (and I) are correct, True. Re-Review the code you have highlighted. `_transientDisposables` is a list of transient objects that implement `IDisposable`. The DI container has retained a reference to them and only releases them when the DI container itself is Disposed. There's an article here on the subject - https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines. – MrC aka Shaun Curtis Dec 14 '21 at 08:50
  • I have added `implementing `IDisposable`` to my answer as some reading it might not get the implicit meaning of `Disposed`! – MrC aka Shaun Curtis Dec 14 '21 at 08:54
  • Well, at least the article is still current. But I was wrong, the container does Dispose Transient object. Luckily the post also states you shouldn't use that, it's silly and can leak memory. – H H Dec 14 '21 at 11:38
  • `Any service that the built-in container creates in order to fill a dependency, which also implements IDisposable, will be disposed by the container at the appropriate point. So Transient and Scoped instances will be disposed at the end of the request (or more accurately, at the end of a scope), and Singleton services will be disposed when the application is torn down and the ServiceProvider itself is disposed.` : https://andrewlock.net/four-ways-to-dispose-idisposables-in-asp-net-core/#automatically-disposing-services-leveraging-the-built-in-di-container – enet Dec 14 '21 at 11:47
  • https://stackoverflow.com/a/40845639/6152891 – enet Dec 14 '21 at 11:48
  • If you're contradicting my statement then you are saying, IDisposable Transient Services get disposed when they go out of scope, not when their Service Container goes out of scope and is destroyed? – MrC aka Shaun Curtis Dec 14 '21 at 14:31
  • @HenkHolterman. Yes the service container does dispose of them as part of it's own `Dispose`, as shown in the `ServiceProvider` code enet pointed us at. You can call dispose manually on any transient service whenever you want, but (unless I'm wrong again), if the service container has a reference to the object in `_transientDisposables` then the garbage collector won't destroy it. That reference isn't cleared until the ServiceProvider's `Dispose` is run. – MrC aka Shaun Curtis Dec 14 '21 at 14:43