Note: this article is published on 07/28/2024.

Because this article is derived from two serieses of articles, so I will list two base serieses below, if you want to view the base serieses you can eazy to navigate to the locations.

A - Introduction

The content of this article

B - Use AddDbContext Extension Method to Register DbContext

To regiester DbContext into .Net Core Container, we do not use the methods, such as

instead, we use AddDbContext Extension Method from EntityFrameworkServiceCollectionExtensions:

this method is more pwerful

public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddDbContext<TContext> 
(this Microsoft.Extensions.DependencyInjection.IServiceCollection serviceCollection, 
Microsoft.Extensions.DependencyInjection.ServiceLifetime contextLifetime, 
Microsoft.Extensions.DependencyInjection.ServiceLifetime optionsLifetime = 
Microsoft.Extensions.DependencyInjection.ServiceLifetime.Scoped) where TContext : 
Microsoft.EntityFrameworkCore.DbContext;

than other three methods, such as AddSingleton:

public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddSingleton 
(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, 
Type serviceType, 
object implementationInstance);

C - DbContext is Regustered as Scoped Lifetime

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

This is safe from concurrent access issues in most ASP.NET Core applications because there is only one thread executing each client request at a given time, and because each request gets a separate dependency injection scope (and therefore a separate DbContext instance).

We use the code combined from these two articles:

i.e., from article top, regester three methods with different lifetimes, such as Singleton, Scoped, and Trensient, while from the second article, regester an entity framework DbContext by AddDbContext method:

The lifetime for the entity framework DbContext is scoped by default, as we discussed previously.

Now, we add two more entity framework DbContexts, as ...Context0 and ...Context2 with explicit named lifetime as Singleton and Transient:

Then we got the results:

Request 1

Request 2

The results:

The DbContext by default is alwarys scoped as light brown color; while Singleton is light green, and Transient is dark brown.

D - Why Scoped for DbContext

The following is mainly from Microsoft [ref].

The lifetime of a DbContext begins when the instance is created and ends when the instance is disposed. A DbContext instance is designed to be used for a single unit-of-work. This means that the lifetime of a DbContext instance is usually very short. In many web applications, each HTTP request corresponds to a single unit-of-work. This makes tying the context lifetime to that of the request a good default for web applications.

To quote Martin Fowler from the link above, "A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work."

A typical unit-of-work when using Entity Framework Core (EF Core) involves:

Important

Why not Singleton or Transient

This argument is mainly from [ref].

Having a single DbContext for the whole application is a Bad Idea. The only situation where this makes sense is when you have a single-threaded application and a database that is solely used by that single application instance. The DbContext is not thread-safe and since the DbContext caches data, it gets stale pretty soon. This will get you in all sorts of trouble when multiple users/applications work on that database simultaneously which is very common of course. (for more information about why a single DbContext -or even on context per thread- is bad, read this answer).

Registering a DbContext as transient could work, but typically you want to have a single instance of such a unit of work within a certain scope. In a web application, it can be practical to define such a scope on the boundaries of a web request; thus a Per Web Request lifestyle. This allows you to let a whole set of objects operate within the same context. In other words, they operate within the same business transaction.

If you have no goal of having a set of operations operate inside the same context, in that case the transient lifestyle is fine, but there are a few things to watch:

References: