该解决方案有几个不同的移动部件,IMO 并不是 Ninject 的详细记录部分。好处是,在实施了这样的事情之后,您应该很快就开始对 Ninject 感到满意了!
首先,您需要更改对象的范围,以便它们使用 HttpContext(如果存在),如果不存在,则使用当前线程作为后备。没有这方面的文档,但有一个 DefaultScopeCallback 不久前被添加到设置中。将该属性设置为您自己的范围回调,该回调使用Ninject.Web.Common source 中的相同代码来获取 HttpContext,然后使用“?? Thread.CurrentThread”作为后备。在安装 NuGet 包时应该自动创建的 CreateKernel 代码中执行此操作。
(我已将 StandardScopeCallbacks.Thread(ctx) 替换为我曾经拥有 Thread.CurrentThread 的位置,因为可以想象前者可能会在某个时候发生变化。目前这两个在他们的工作上是相同的。)
private static IKernel CreateKernel()
{
var settings = new NinjectSettings{ DefaultScopeCallback = DefaultScopeCallback };
var kernel = new StandardKernel(settings);
// The rest of the default implementation of CreateKernel left out for brevity
}
private static Object DefaultScopeCallback(Ninject.Activation.IContext ctx)
{
var scope = ctx.Kernel.Components.GetAll<INinjectHttpApplicationPlugin>()
.Select(c => c.GetRequestScope(ctx)).FirstOrDefault(s => s != null);
return scope ?? Ninject.Infrastructure.StandardScopeCallbacks.Thread(ctx);
}
另外,不要忘记内核需要作为静态对象留出供以后访问。您不想每次需要时都更新新内核;我通过“MyConfig.ObjectFactory”使我的访问。虽然这是服务定位器反模式的代码味道,但我们将竭尽全力尽可能避免反模式。
其次,根据提交描述,DefaultScopeCallback 只影响没有显式作用域的显式绑定。因此,如果像我一样,您依赖于一堆未添加的隐式绑定,那么您现在需要配置它们:
kernel.Bind(i => i.From(Assembly.GetExecutingAssembly(), Assembly.GetAssembly(typeof(Bll.MyConfig)))
.SelectAllClasses()
.BindToSelf());
如果您不喜欢上述操作,还有另一种方法可以为所有隐式绑定设置默认范围,这可以说是更优雅。 Changing default object scope with Ninject 2.2
第三,如果您想在每个 Parallel 操作结束时清除范围内的所有缓存对象,以便内存使用量不会因 EF 缓存或其他原因而飙升,这里是如何清除范围为当前的 Ninject 缓存线程:
Parallel.ForEach(myList, i =>
{
var threadDb = MyConfig.ObjectFactory.Get<MyContext>();
CreateModelsForItem(i, threadDb);
MyConfig.ObjectFactory.Components.Get<Ninject.Activation.Caching.ICache>().Clear(Thread.CurrentThread);
});
请注意,我在最后没有使用 Clear 行的情况下进行了一些测试,即使 HttpRequest 完成并且我多次生成报告,似乎 EF 上下文也被重新使用了。这不是我想要的,所以清除操作很重要。确实,我想要的行为更接近 InCallScope,但尝试使用 InCallScope 作为后备的 InRequestScope 是我将在另一天打开的一罐蠕虫。