Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use both AddDbContextFactory() and AddDbContext() extension methods in the same project

I'm trying to use the new DbContextFactory pattern discussed in the DbContext configuration section of the EF Core docs.

I've got the DbContextFactory up and running successfully in my Blazor app, but I want to retain the option to inject instances of DbContext directly in order to keep my existing code working.

However, when I try to do that, I'm getting an error along the lines of:

System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.EntityFrameworkCore.IDbContextFactory1[MyContext] Lifetime: Singleton ImplementationType: Microsoft.EntityFrameworkCore.Internal.DbContextFactory1[MyContext]': Cannot consume scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions1[MyContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory1[MyContext]'.) ---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.EntityFrameworkCore.IDbContextFactory1[MyContext] Lifetime: Singleton ImplementationType: Microsoft.EntityFrameworkCore.Internal.DbContextFactory1[MyContext]': Cannot consume scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions1[MyContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory1[MyContext]'. ---> System.InvalidOperationException: Cannot consume scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions1[MyContext]' from singleton 'Microsoft.EntityFrameworkCore.IDbContextFactory1[MyContext]'.

I also managed to get this error at one point while experimenting:

Cannot resolve scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions`1[MyContext]' from root provider.

Is it theoretically possible to use both AddDbContext and AddDbContextFactory together?

like image 646
tomRedox Avatar asked Nov 26 '20 13:11

tomRedox


People also ask

What is DbContextFactory?

DbContextFactory provides you extensions to inject the DbContext as a factory using the Microsoft default implementation of dependency injection for Microsoft. Extensions.

Should DbContext be singleton or transient?

DbContext should not be used as a singleton because it is holding a connection object which cannot be used by multiple threads at the same time.

Which of the below method is used for adding DbContext class as service?

The AddDbContext extension method registers DbContext types with a scoped lifetime by default.

Is DbContext scoped or transient?

One common usage of this approach would be any database logic implementing classes, since one connection can be allowed within a request. The DbContext class of the Entity Framework core, when declared would result in a scoped instance.


1 Answers

It is, it's all about understanding the lifetimes of the various elements in play and getting those set correctly.

By default the DbContextFactory created by the AddDbContextFactory() extension method has a Singleton lifespan. If you use the AddDbContext() extension method with it's default settings it will create a DbContextOptions with a Scoped lifespan (see the source-code here), and as a Singleton can't use something with a shorter Scoped lifespan, an error is thrown.

To get round this, we need to change the lifespan of the DbContextOptions to also be 'Singleton'. This can be done using by explicitly setting the scope of the DbContextOptions parameter of AddDbContext()

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

There's a really good discussion of this on the EF core GitHub repository starting here. It's also well worth having a look at the source-code for DbContextFactory here.

Alternatively, you can also change the lifetime of the DbContextFactory by setting the ServiceLifetime parameter in the constructor:

services.AddDbContextFactory<FusionContext>(options => 
    options.UseSqlServer(YourSqlConnection), 
    ServiceLifetime.Scoped);

The options should be configured exactly as you would for a normal DbContext as those are the options that will be set on the DbContext the factory creates.

like image 176
tomRedox Avatar answered Sep 20 '22 01:09

tomRedox