We are implementing DAL with EF Core and Postgres Npgsql provider.
Some tables have jsonb columns and we map them to JsonDocument properties in entity objects.
The page JSON Mapping in Npgsql documentation has the following note:
Note also that JsonDocument is disposable, so the entity type is made disposable as well; not dispose the JsonDocument will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework.
According to the doc, we have made the entities disposable:
public class SomeEntity : IDisposable
{
public int Id { get; set; }
public JsonDocument JsonData { get; set; }
public void Dispose()
{
JsonData?.Dispose();
}
}
The problem is that Dispose method for entity objects is never called while DbContext is disposed correctly. The only way we see is to manually enumerate DbSet when DbContext is disposed and invoke Dispose method for each entity:
public class SomeDbContext : DbContext
{
public DbSet<SomeEntity> SomeEntities { get; set; }
private bool DisposedEntities { get; set; }
public override void Dispose()
{
DisposeEntities();
base.Dispose();
}
public override ValueTask DisposeAsync()
{
DisposeEntities();
return base.DisposeAsync();
}
private void DisposeEntities()
{
if (DisposedEntities)
{
return;
}
DisposeEntities(SomeEntities);
DisposedEntities = true;
}
private static void DisposeEntities<TEntity>(IEnumerable<TEntity> dbSet)
where TEntity : IDisposable
{
foreach (var entity in dbSet)
{
entity.Dispose();
}
}
}
Is there a way to force EF Core to dispose entities when DbContext is disposed?
Is above approach with manual entities dispose OK or it has some flaws?
While your approach will work, the intent isn't generally for the DbContext to "own" the entity instances it hands out (and therefore to dispose them when it itself is disposed). You use a DbContext to get out instances, but at that point they are yours - it's perfect reasonable for them to outlive the context through which they were created.
In other words, it really is up to your application code to call Dispose when done with an entity instance.
Note also that if Dispose isn't called, there's no leak or anything, and so no catastrophic problem; all it means is that GC pressure will be higher, possibly causing some performance degradation.
Not OK. Your implementation loads whole table during dispose, because when enumerating DbSet - it loads data from database.
Consider to change function signature and dispose entities from Local collection:
private static void DisposeEntities<T>(DbSet<T> dbSet)
where T : class, IDisposable
{
foreach (var entity in dbSet.Local)
{
entity.Dispose();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With