Diego Mijelshon
Diego Mijelshon

Reputation: 52745

How do I clear a large persistent collection quickly?

I have an entity Foo with a Bars collection.

This collection is quite big (say, 20,000 elements, and yes, it needs to be loaded entirely, because it's displayed in a TreeView), and the elements are generated automatically based on some parameters of the Foo that the user can change, using a WPF MVVM application (all processing occurs in the client)

After he is satisfied with the results, he hits save, and DbContext.SaveChanges() is called.

Generating Bars implies clearing the collection, then doing a bunch of calculations and adding the new elements. When this is a new, non-persistent Foo, it's no problem because I can just call foo.Bars.Clear()

The problem is, doing that with already persistent entities causes EF to set bar.Foo = null in all the elements, instead of deleting them.

My workaround goes like this:

void Regenerate()
{    
    if (fooIsPersistent)
    {
        foreach (var bar in foo.Bars.ToList())
            context.Entry(bar).State = EntityState.Detached;
        deleteOnSave = true;
    }
    else
        foo.Bars.Clear();
    AddTheNewCalculatedElements()
}

void Save()
{
    if (deleteOnSave)
        context.Database.ExecuteSqlCommand("delete Bar where Foo = @Id", param);
    context.SaveChanges();
}

The problem with this solution is that it doesn't scale. Calling Entry(bar).State = EntityState.Detached when there are many entities loaded is even slower than just calling context.Set<Bar>().Remove(bar) and letting EF do the deletes one by one, so it defeats the purpose of the workaround.

So, my question is: Is there any efficient way to make EF believe the collection is actually empty?

Or should I do it in a completely different way?

Upvotes: 1

Views: 141

Answers (1)

Diego Mijelshon
Diego Mijelshon

Reputation: 52745

Well, it was easier than I thought.

I replaced this line:

context.Entry(bar).State = EntityState.Detached;

with this one (which I thought was more or less equivalent):

((IObjectContextAdapter)context).ObjectContext.Detach(bar);

And it now completes in a reasonable time.

Upvotes: 1

Related Questions