1

From the documentation of MSDN on DbContext and DbContext pooling:

DbContext pooling has a few limitations on what can be done in the OnConfiguring method of the context.

Avoid using context pooling in apps that maintain state. For example, private fields in the context that shouldn't be shared across requests. EF Core only resets the state that it is aware of before adding a context instance to the pool.

Can you provide a concrete example on how these limitations would work?

Stefan
  • 17,448
  • 11
  • 60
  • 79
variable
  • 8,262
  • 9
  • 95
  • 215
  • This question is only about context pooling. Not connection pooling. I think the description is clear enough. Go figure what may happen if you store a tenant ID in a context instance. – Gert Arnold Feb 19 '22 at 18:47

1 Answers1

2

Special thanks to Andrew Williamson for his splendid edit:


So this:

Avoid using context pooling in apps that maintain state. For example, private fields in the context that shouldn't be shared across requests. EF Core only resets the state that it is aware of before adding a context instance to the pool.

Basically states, that if you use private fields in your DbContext, like _myPrivateField, or a private property for that matter, and you use this to do some calculations - using context pooling might result in undesired behavior because you don't know what the initial value will be.

As an example, let's say you have a requirement to log the total number of rows affected during your request. The easiest way to do this would be to override the DbContext to track this:

public class FooContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }

    public int Updates { get; private set; } = 0;

    public override int SaveChanges()
    {
        Updates += base.SaveChanges();
    }
}

Now if we turn on context pooling:

  • Request 1 creates a new FooController, which gets a new DbContext
  • Request 1 updates a Foo and saves changes, which increments the Updates value by 1
  • Request 1 logs the message 1 rows affected
  • Request 2 creates a new FooController, which reuses the same DbContext
  • Request 2 updates a Foo and saves changes, which increments the Updates value by 1
  • Request 2 logs the message 2 rows affected, which is not right

Entity Framework resets the things that it knows about before giving out a pooled DbContext, but since Updates is a custom variable, it doesn't know to reset it.

This one is easier:

Context pooling is intended for scenarios where the context configuration, which includes services resolved, is fixed between requests. For cases where Scoped services are required, or configuration needs to be changed, don't use pooling.

Simply states that if you need different configuration options for each DbContext - pooling is not able to help due to the re-use of the DbContext (and hence, the configuration). An example of this might be a multi-tenant environment where each tenant has their own database:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(CurrentUser.Tenant.ConnectionString);
}

If context pooling is turned on, the connection string will be cached between requests, so a user might start seeing data from a tenant they don't belong to instead of their own data.


About connection pooling specific, the doc's state that:

Note that DbContext pooling is orthogonal to database connection pooling, which is managed at a lower level in the database driver.

In this sentence "orthogonal" is just a difficult way of saying DbContext pooling and connection pooling are independent of each other because connection pooling is handled by a different mechanism.


Here is a nice article which explains why pooling is helpful: https://neelbhatt.com/2018/02/27/use-dbcontextpooling-to-improve-the-performance-net-core-2-1-feature/

Stefan
  • 17,448
  • 11
  • 60
  • 79
  • 1
    `DbContext` is not thread safe, a pooled context will not be given to two threads at the same time. The issue is consecutive access with an unknown initial state, not concurrent access. Do you mind if I expand your answer? – Andrew Williamson Feb 16 '22 at 19:57
  • @AndrewWilliamson: wow, thanks. – Stefan Feb 16 '22 at 20:27
  • Hi guys - EF dbcontext connection pooling results in the instance being pooled (upto max 1024) so that subsequent controller action can re-use an instance from the pool. 1) Do the instances in the pool maintain an active db connection? 2) Suppose dbcontext pooling isn't setup then I know this will instantiate a new dbcontext for each controller action, but any idea if sql server has its own internal reusable connection pool? – variable Jul 10 '22 at 10:22
  • Hi @variable; I think that would be sufficient for its own question. I would need to dig into it, but preferably the connection stays open for at least a bit. You could use a profiler to double check quickly I guess – Stefan Jul 10 '22 at 17:07