New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Injecting a DBContext with AddDbContext and UseNpgsql causes it to remain in memory after the scope has been disposed of #3132
Comments
After a bit more digging, found this todo item. Applying it seems to have fixed the issue. It was NpgsqlSingletonOptions.ApplicationServiceProvider that was holding a reference to my db context instance preventing it from being garbage collected. |
@paulnsk I've looked at your sample code, and I'm not quite following... The fact that a DbContext instance shows up in memory after it was disposed is expected: .NET uses a garbage collector which kicks in at arbitrary times, and objects can stay in memory for a long time after they're disposed and unreachable before they get collected. The question is whether the objects can get collected, i.e. whether they're still rooted (referenced) in some way that prevents the garbage collector from collecting them. I put together a quick sample based on your code that repeatedly creates a new scope and context in a loop (see code below), and ran that with a memory profiler; as expected, memory usage is very stable and no memory leak is apparent. Code samplevar builder = Host.CreateApplicationBuilder();
builder.Services.AddDbContext<MyContextForInjection>
(
options =>
{
//bug!!
options.UseNpgsql("foo");
//no bug
//options.UseSqlServer("foo");
}
);
using var app = builder.Build();
for (var i = 0; i < 1000000; i++)
{
var serviceProvider = app.Services.GetRequiredService<IServiceProvider>();
using (var skope = serviceProvider.CreateScope())
{
using (var myContext = skope.ServiceProvider.GetRequiredService<MyContextForInjection>())
{
}
}
}
public class MyContextSimple: DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseNpgsql("foo");
}
}
public class MyContextForInjection(DbContextOptions<MyContextForInjection> options) : DbContext(options);
Can you try to reproduce this in a minimal, runnable code sample? |
Yes, it is referenced and hence not collected by GC. Otherwise it would only be shown by VS memory profiler when "Show dead objects" is checked. In fact, it is referenced specifically by |
@paulnsk OK, thanks for the added info, I'll look deeper into this. |
I am new to Postgres so if this behavior is somehow by design, I apologize.
Here is my DBContext with nothing in it
Here is me registering it with for the dependency injection as a scoped service:
Note the bogus connection string, so no actual connection to a DB is even being made.
Now let's try using it:
While the app is waiting for ReadLine() go to Visual Studio Diagnostic tools, take memory snapshot and inspect it.
This does not happen with MS SQL (
UseSqlServer
).This does not happen when the DBContext is
new
ed up manually (in which caseUseNpgSql
is called from itsOnConfiguring()
method).Here is a sample project.
UseNpgSqlBug.zip
In real life, when an actual connection to a DB is made followed by some data retrieval or insertion, all the tracked entitites remain in memory, in my case it was hundreds of megabytes. Curiously though, if I put the whole
using
block in a loop, I don't see multiple instances of the DBContext, just one. So it behaves kind of like a singleton (or maybe the old one does get destroyed when a new one is created).The text was updated successfully, but these errors were encountered: