Today I ran into an interesting issue. I have a process that uses many database Repositories that are intended to share a DbContext. This is necessary due to a reliance, and desire to use, Entity Framework’s state tracking.
However, once I moved away from working entirely from an HttpContext scope, my Ninject bindings failed me.
Generally, for an Entity Framework’s DbContext, and since I mostly do web-based development, binding to the request scope has been reliable. This is the general mechanism I use:
Bind<MyContext>().ToSelf().InRequestScope(); Bind<IRepository<MyClass1>().<Repository1>(); Bind<IRepository<MyClass2>().<Repository2>(); Bind<IService().<MyService>();
This works perfectly well with .NET’s Mvc and WebApi controllers. But, this because a problem once using a Thread/Task to run my process. Each IRepository wound up getting a completely new DbContext. As such, when checking the Entity Framework’s Entry State, objects would, due to unknowingly checking the wrong context, be marked as “Added” rather than “Unchanged.”
That bit of code defining a few repos and my service would look something like this:
public class Repository1 : BaseRepository<MyClass1> { public Repository1(MyContext dbContext) : base(dbContext) { } } public class Repository2 : BaseRepository<MyClass2> { public Repository2(MyContext dbContext) : base(dbContext) { } } public class MyService { public MyService(IRepository<MyClass1> repo1, IRepository<MyClass2> repo2) { .... } }
In the thread context, when using Task.Run, what happens is that a new DbContext is passed into each repository. This behavior can be seen directly:
Task.Run(() => { var svc= ((IService)GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IService))); svc.RunProcess(); });
Long story short, since I need all repositories that are used within the service to share the same DbContext, we can use Ninject’s more generic InScope() method that takes a Func<IContext, object> to dynamically specify the scoping.
All we need to check for is the existence of HttpContext.Current. If it doesn’t exist, we return the current thread. Our new binding for the DbContext would accordingly look like this:
Bind<MyContext >().ToSelf().InScope(x => { return (HttpContext.Current != null ? (object)HttpContext.Current : (object)Thread.CurrentThread);
This small change to the binding makes the dependency resolution / scoping behave the way I expect.
One thought on “Ninject Scoping between Threads and HttpContext”