Reputation: 1678
I'd like to use a Ninject custom scope within my application in order to contain the scope of a single activation of a DbContext to my core domain handler. I'm having trouble however, because the CommandScope object implements the INotifyWhenDisposed interface, which is a part of Ninject, and I don't want to take a dependency on Ninject within my domain.
I've tried a number of other ways to get the dependency into the code, with no success, including the use of Ninject Factories to expose an IScopeFactory, and a Func dependency. In the latter case, the problem is that (I think) Ninject does not wire up INotifyWhenDisposed.Dispose event because the binding target is Func not an IDisposable itself.
Anyway, here's the code of what I'm trying to achieve.
IoC
Kernel
.Bind<MyDbContext>()
.ToSelf()
.InScope(x => CommandScope.Current)
.OnDeactivation(x => x.SaveChanges());
CommandScope
public class CommandScope : INotifyWhenDisposed
{
public event Dispose;
public bool IsDisposed { get; private set; }
public static CommandScope Current { get; private set; }
public static CommandScope Create()
{
CommandScope result = new CommandScope();
Current = result;
return result;
}
public void Dispose()
{
IsDisposed = true;
Current = null;
Dispose?.Invoke(this, EventArgs.Empty);
}
}
Inside my domain...
public class Pipeline<TRequest, TResponse>
{
readonly IRequestHandler<TRequest, TResponse> innerHandler;
public Pipeline(IRequestHandler<TRequest, TResponse> handler)
{
innerHandler = handler;
}
public TResponse Handle(TRequest request)
{
using(CommandScope.Create())
{
handler.Handle(request);
}
}
}
Upvotes: 0
Views: 540
Reputation: 5427
clearing the scope object from Ninject's ICache
will force Ninject to close and dispose all instances controlled by the scope:
var activationCache = Kernel.Get<Ninject.Activation.Caching.ICache>();
activationCache.Clear(CommandScope.Current);
you may also consider the NamedScope extension to have better alternatives to your singleton pattern CommandScope.
Upvotes: 0
Reputation: 13233
You could use the decorator pattern (requires an interface) and only have the decorator implement the INotifyWhenDisposed
interface - then put the decorator in the composition root - there you need a ninject reference anyway.
Alternatevily, you can use a Func Factory, create an IDisposable
that actuall implements INotifyWhenDisposed
(the consuming library however doesn't need to know about this). When accessing the instance in your composition root, you can still cast it to INotifyWhenDisposed
(might actually not be necessary). For example:
.InScope(x => (INotifyWhenDisposed)CommandScope.Current)
A comment on the design: I would highyl recommend to split CommandScope
into two classes: a factory and the actual scope.
Also, in the long run you can probably save a lot of headache by making sure that a new scope (created by Create
) does not replace an old one that has not been disposed yet. Otherwise you might very well miss a leak you introduce.
Upvotes: 2