1

I have a collection of System.Threading.Timer that run in the background of a webapp that periodically retrieve data from a DB to update the cache.

Occasionally (not often), calls to the DB will fail with an error such as "The ObjectContext instance has been disposed and can no longer be used for operations that require a connection".

It seems like it's sometimes being disposed by another thread, but when each timer runs, it'll create a brand new DbContext, so I'm not sure how that can be the case.

I'm using StructureMap to create a nested container, so that it runs in isolation, and I've verified that it does indeed create a new DbContext for each timer.

var a = new A();
var b = new B();

var timer1 = new Timer(x => a.Update(container), null, TimeSpan.Zero, TimeSpan.FromMinutes(60));
var timer2 = new Timer(x => b.Update(container), null, TimeSpan.Zero, TimeSpan.FromMinutes(60));

public class A 
{
    public void Update(IContainer container)
    { 
        using (var nestedContainer = container.GetNestedContainer()) {
            // This will create a new DbContext and will be disposed 
            // when the nested container is disposed
            var repository = nestedContainer.GetInstance<IRepositoryA>();

            // Sometimes fails here when it's accessing the DbContext
            repository.GetStuff();
        }
    }
}

public class B
{
    public void Update(IContainer container)
    { 
        using (var nestedContainer = container.GetNestedContainer()) {
            var repository = nestedContainer.GetInstance<IRepositoryB>();

            repository.GetStuff();
        }
    }
}

Here's the StructureMap config. So whenever a repository is created it will create a new factory which in turn creates a new MyDbContext.

For<IDbContextFactory<MyDbContext>>().HybridHttpOrThreadLocalScoped()
    .Use(() => new DbContextFactory());
Tom
  • 1,561
  • 4
  • 20
  • 29
  • Have a look at: https://stackoverflow.com/questions/12827599/parallel-doesnt-work-with-entity-framework – Rand Random Mar 14 '18 at 16:10
  • Hmm, that just seems to be saying that DbContext isn't thread safe. But I'm creating a new instance in each thread, so it shouldn't be shared. – Tom Mar 14 '18 at 16:31

1 Answers1

0

It turns out this was being caused by the HybridHttpOrThreadLocalScoped StructureMap plugin, which disposed of the DbContext when the local thread disposed of the nested container, so other threads could no longer use it. Removing that and changing my StructureMap config to use a nested container for the lifecycle of an HTTP request resolved it.

Tom
  • 1,561
  • 4
  • 20
  • 29