我想在我的应用程序中使用Ninject自定义作用域,以便将DbContext的单个激活范围包含到我的核心域处理程序中。我遇到了麻烦,因为CommandScope对象实现了INotifyWhenDisposed接口,这是Ninject的一部分,我不想在我的域中依赖Ninject。
我已经尝试了许多其他方法来获取代码中的依赖项,但没有成功,包括使用Ninject Factories来公开IScopeFactory和Func依赖项。在后一种情况下,问题是(我认为)Ninject没有连接INotifyWhenDisposed.Dispose事件,因为绑定目标是Func而不是IDisposable本身。
无论如何,这是我想要实现的代码。
的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);
}
}
在我的域内...
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);
}
}
}
答案 0 :(得分:1)
你可以使用装饰器模式(需要一个接口)并且只让装饰器实现INotifyWhenDisposed
接口 - 然后将装饰器放在组合根中 - 无论如何你需要一个ninject参考。
Alternatevily,你可以使用一个Func Factory,创建一个IDisposable
来实现INotifyWhenDisposed
(消费库不需要知道这个)。在组合根中访问实例时,您仍然可以将其强制转换为INotifyWhenDisposed
(实际上可能没有必要)。例如:
.InScope(x => (INotifyWhenDisposed)CommandScope.Current)
对设计的评论:我想高调建议将CommandScope
分成两类:工厂和实际范围。
此外,从长远来看,您可以通过确保新范围(由Create
创建)不会替换尚未处置的旧范围来节省大量头痛。否则你很可能会错过你介绍的漏洞。
答案 1 :(得分:0)
从Ninject的ICache
清除范围对象将迫使Ninject关闭并处置由范围控制的所有实例:
var activationCache = Kernel.Get<Ninject.Activation.Caching.ICache>();
activationCache.Clear(CommandScope.Current);
你也可以认为NamedScope extension有更好的替代单身模式CommandScope。