Search code examples
c#asp.net-coreentity-framework-coreblazor.net-5

Blazor Server: Mixing EF Core DbContextFactory with DbContext


I am building a Blazor Server front end to an existing domain layer. This layer offers various injectable services to do modifications on the EF Core repository. For this, the services itself requests a DbContext from the (standard Microsoft) DI container. This works fine with regular ASP.NET MVC / Razor pages with a scoped DbContext instance, but as documented, this is problematic with Blazor. In a Blazor Server app we’d want to use DbContextFactory to generate short-lived DbContext instances for operations instead.

It’s no problem to have both a DbContext and DbContextFactory in the same application, but I’m struggling to understand how to adapt my services. Or if I even need to? To illustrate, this is the current code:

My page:

@page “/items”
@inject ItemService ItemService

// somewhere in the code
    ItemService.DoOperation(…)

My service

class ItemService
{
    public ItemService(MyDbContext myDbContext)
    {
        // ...
    }

    public bool DoOperation(…)
    {
        // ...
        _myDbContext.SaveChanges();
    }
}

Startup.cs:

services.AddDbContext<MyDbContext>(options => ...),
            contextLifetime: ServiceLifetime.Transient,
            optionsLifetime: ServiceLifetime.Singleton);

services.AddDbContextFactory<MyDbContext>(options => ...);

I’ve changed the lifetimes for DbContext according to the example given in this answer and so far I haven’t been able to create any issues, but I don’t fully understand the lifetime issues at play here. How can I engineer my service to play well in both a Blazor and an ASP.NET MVC/Razor pages application in an obvious way?


Solution

  • The problem is transitive: Your services rely on a should-be-scoped resource and that only works when you register those services as scoped as well. Which you can't.

    The proper way is to rewrite your services to the DbContext per Operation model, and inject the DbContextFactory.

    It looks like you already have a mixed model (with a SaveChanges per operation they are actually a UoW).

    When you don't want to make those changes you could weasel your way out by registering the DbContext as Transient. That feels bad but it's designed to quickly release the underlying connection. So it's not the resource leak that it looks like.