2

I am using ASP.NET Core 2.2 with Pomelo.EntityFramework.MySql.

Here is my code:

 services.AddDbContext<dbContext>(options => options.UseMySQL(appConfigsSection["DbConnectionString"]));
 services.AddSingleton<IUserService, UserService>();


 services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(x=> {
                x.Events = new JwtBearerEvents
                {
                    OnTokenValidated = context =>
                    {
                        var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();       
                    }
                };

Here is the error:

asp.net core Cannot consume scoped service from singleton

on the line of:

 var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>(); 

My understanding is the DBContext should be used as a Singleton service, so is the IUserService. But it seems that DBContext is treated as a Scoped Service.

I can easily fix it by switching my IUserService back to Scoped Service. But I am wondering why can't I use DBContext as a Single service?

I think the DBContext should be used as a Singleton service, correct??

here

Michiel
  • 180
  • 2
  • 14
Franva
  • 6,565
  • 23
  • 79
  • 144
  • what make you think that `DbContext` should be a singleton? – vasily.sib Apr 17 '19 at 07:54
  • Most definitely *NOT*. Even scoped may be too much. A DbContext is like an NHibernate session - transient, single user and disconnected in nature, it shouldn't stay alive any longer than necessary. Just like connections, it's not expensive to create or close thanks to connection pooling. Just like connections, if it lives too long it accumulates changes. If its connection stays open for too long, it accumulates locks leading to blocking in the database and possibly deadlocks. – Panagiotis Kanavos Apr 17 '19 at 08:11
  • Given its disconnected nature a DbContext *caches* changes too, just like a DataTable, which means it can't be used by different threads/users. All these things aren't limitations as they make DbContext *fast* and *scalable*. – Panagiotis Kanavos Apr 17 '19 at 08:13
  • A meaningful question would be `why scoped instead of transient`? During a web request, if the code requests the same context multiple times, it would make sense to cache all objects and changes until the end of the request, and persist them just once with a single call to `SaveChanges` at the very end. This would perform better than eg 4 methods during the same request, creating a new CustomersContext context to retrieve and modify the *same* customer, and then save the changes 4 times, potentially overwriting each other's changes – Panagiotis Kanavos Apr 17 '19 at 08:17

1 Answers1

8

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. You will run into errors if two requests try to use it at the same time. If your service depends on the context, the service cannot be a singleton.

Scoped makes sense since it allows for you to pass around DB objects between services and get the same DbContext in all of them so you can query entities in one service and save the changes in another.

You can change it to transient if you need to run queries in parallel in two services for example. The service lifetime is a parameter on AddDbContext().

juunas
  • 54,244
  • 13
  • 113
  • 149
  • Could you be more specific? Where would you expect a race condition to be created and with what settings? – juunas Apr 17 '19 at 07:38
  • 1
    This is not the only reason DbContexts should be short-lived - just think about ChangeTracking, Relationship fixup and Concurrency issues. – DevilSuichiro Apr 17 '19 at 08:14
  • 1
    The main problem isn't thread-safety. Even if it were thread-safe, a perpetual connection would result in perpetually held locks in the database. A DbContext is meant to be disconnected until `SaveChanges` is called though and therefore caches changes. If two or more threads/requests accessed the *same* context, they'd end up affecting each other's objects and changes. – Panagiotis Kanavos Apr 17 '19 at 08:14
  • 1
    A DataTable has pretty much the same lifecycle. Change tracking, disconnected, not meant to live for long – Panagiotis Kanavos Apr 17 '19 at 08:15