Ninject - In what scope DbContext should get binded when RequestScope is meaningless?
Asked Answered
A

1

15

In an MVC / WebAPI environment I would use InRequestScope to bind the DbContext.

However, I am now on a Console application / Windows service / Azure worker role (doesn't really matter, just there's no Web request scope), which periodically creates a number of Tasks that run asynchronously. I would like each task to have its own DbContext, and since tasks run on their own thread, I tried binding DbContext using InThreadScope.

Unfortunately, I realize that the DbContext is not disposed when a task is finished. What actually happens is, the thread returns to the Thread Pool and when it is assigned a new task, it already has a DbContext, so DbContexts stay alive forever.

Is there a way InThreadScope can be used here or should I use some other scope? How can ThreadScope be used when threads are returning from ThreadPool every now and then?

Arginine answered 15/4, 2014 at 10:59 Comment(10)
You noticed that the Per Thread Lifestyle is Considered Harmful.Schizopod
@Schizopod Yes, I see. So how to implement custom scope (or other solution)?Arginine
You should not use a scope at all. Use a factory to create the DbContext (which is basically a unit of work) - and pass it around as necessary. If the software stays as simple as you describe it, you could also use a named scope (defined by the Task and bind DbContext InNamedScope). But that is not future proof for example when you want to create DbContext outside of a task or when you want to have multiple (sequential) DbContext inside a single task,... etcInly
I tend to disagree with @Inly on this, but it depends on your application what the best approach is. IMO 'right' solution is to explicitly start a 'scope' per operation, although I don't know how to do that in Ninject. If written about when to use a factory and when to inject the UoW itself, here. The answer talks about web apps, but this is applicable to any application that handles requests of some sort.Schizopod
@Steven, i completely agree, but i also think that once you've used "Method Injection" for a while a pattern should emerge automagically on how you should abstract it best. Not the perfect answer for now but a way to get to better code mid-term to long term ;-). We are storing the DbContext in a thread-local so we don't need method-injection. This works perfectly for us (as described here: #21675090).Inly
@BatteryBackupUnit: One interesting quote from your anser 21674590 is "Once all operations are done, we release the DbContext and reset the ThreadLocal<DbContext>.Value.". By doing this you created your own 'scope': i.e. the lifetime of the DbContext is clearly defined and ends at the end of the request.Schizopod
@Schizopod I agree 100%. Just to clarify it for other readers: Ninject features a concept of "scope". Binding "in" a scope tells ninject when to create an instance and when to reuse one. We did not use that kind of scope. Also i would not exactly say that we couple it to the request. We've got WCF requests and we've got events comming from hardware devices. Some of the WCF requests or events may instruct us to do several things which may require several unit of works, sometimes even async or at a later point (like scheduled or what not). Sometimes operations requiring unit of work may be nested.Inly
@BatteryBackupUnit: Seems we're on the same wavelength here. Since you know most about Ninject, would you like to answer the question? Perhaps giving the OP the possible options there are. You'll get my +1.Schizopod
@Inly and Steven so is there any solution ??!! ^_^Angeloangelology
@WahidBitar yes: 1) use a factory to create a UnitOfWork, pass it around, and dispose it at the end, 2) use a factory to create a UnitOfWork, and use something like a "TaskLocal" to access it without having to pass it around.Inly
R
2

If you decide to go on with custom scope, the solution is:

public sealed class CurrentScope : INotifyWhenDisposed
{
    [ThreadStatic]
    private static CurrentScope currentScope;

    private CurrentScope()
    {
    }

    public static CurrentScope Instance => currentScope ?? (currentScope = new CurrentScope());

    public bool IsDisposed { get; private set; }

    public event EventHandler Disposed;

    public void Dispose()
    {
        this.IsDisposed = true;
        currentScope = null;
        if (this.Disposed != null)
        {
            this.Disposed(this, EventArgs.Empty);
        }
    }
}

Binding:

Bind<DbContext>().To<MyDbContext>().InScope(c => CurrentScope.Instance)

And finally:

using (CurrentScope.Instance)
{
    // your request...
    // you'll get always the same DbContext inside of this using block
    // DbContext will be disposed after going out of scope of this using block
}
Rociorock answered 7/1, 2018 at 14:13 Comment(2)
Do you have to resolve your dependencies inside the using block for this to work?Aframe
@IronSean, yes using block defines a lifetime of your dependency.Rociorock

© 2022 - 2024 — McMap. All rights reserved.