I already checked this using a memory profiler and there are no real entities which stay in memory but hash-sets, dictionaries and EntityKey objects -- but I found no way how to disconnect these references.
So simple question: How do I stop the context (or its ObjectStateManager) from growing infinitely in size?
[And yes, I know that long living contexts should be avoided, but in this case it's one complex analysis run which needs several hierarchical data being loaded (and the sample below is just a minimal problem demonstration) so finally it is a "short" living one-operation context.]
Steps to repro:
Code [Updated, doesn't need real DB connection anymore]:
class Program
{
static void Main()
{
const double MiB = 1024 * 1024;
using ( var context = new NorthwindEntities() )
{
var last = GC.GetTotalMemory(true) / MiB;
Console.WriteLine("before run: {0:n3} MiB", last);
var id = 0;
while ( true )
{
Run(context, ref id);
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
var current = GC.GetTotalMemory(true) / MiB;
Console.WriteLine("after run: {0:n3} MiB (+{1:n3} MiB)", current, current - last);
last = current;
if ( Console.KeyAvailable )
break;
Console.WriteLine(new string('-', 100));
}
}
}
static void Run(NorthwindEntities context, ref int id)
{
for ( int i = 0; i < 100000; i++ )
{
var category = new Category { Category_ID = ++id };
category.EntityKey = new EntityKey("NorthwindEntities.Categories", "Category_ID", id);
var product = new Product { Product_ID = id, Category_ID = id };
product.EntityKey = new EntityKey("NorthwindEntities.Products", "Product_ID", id);
product.Category = category;
context.Attach(product);
context.Detach(product);
context.Detach(category);
}
var ctr = 0;
Console.WriteLine("Enumerating living/attached objects:");
const EntityState AllStates = EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged;
foreach ( var entry in context.ObjectStateManager.GetObjectStateEntries(AllStates) )
Console.WriteLine(" #{0} [{1}] {2}", ++ctr, entry.EntityKey, entry.Entity);
if ( ctr == 0 )
Console.WriteLine(" NOTHING (as expected)");
}
}
Since I'm only detaching entities directly after having called SaveChanges(), I'm now counting the number of detached entities and when the counter reaches 10,000 I detach all still living (and needed) objects from the context and create a new context to which I attach all detached objects. Downside: The IsLoaded property of EntityReferences and EntityCollections is now always false (but I don't rely on this).
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