如何在使用Ninject的Web应用程序中使用线程作用域进行并行操作

无论何时使用新的DI框架,我都会重复运行相同的问题......如何从HttpRequest中启动大规模并行操作,其中每个线程都需要其自己唯一的依赖副本? 就我而言,我正在使用Ninject。

我经常遇到的特定情况是CPU密集型报告,使用Parallel.ForEach,需要使用实体框架DbContext; EF上下文对于线程必须是唯一的,但在这些特殊报告之外,EF上下文必须是InRequestScope。

Ninject如何实现这一目标? 最好允许将每个任务的EF上下文配置在Parallel.ForEach上,因为使用上下文加载的数据只会停留在上下文中并占用内存。

请注意,此报表足够大以保证Parallel.ForEach,但足够小,以便它可以在Web请求中同步运行,而不会超时浏览器(<60秒)。 也许我是怪异,但我碰到这个需要很多


该解决方案有几个不同的移动部件,IMO,Ninject没有非常有据可查的部分。 好处是,在执行这样的事情之后,你应该开始对Ninject感到舒服!

首先,您需要更改对象的范围,以便它们在存在时使用HttpContext,如果不存在,则使用当前线程作为后备。 没有这方面的文档,但有一个DefaultScopeCallback被添加到设置中。 将该属性设置为您自己的范围回调,它使用Ninject.Web.Common源代码中的相同代码获取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());

如果你不喜欢这样做,还有另一种方法可以为所有隐式绑定设置默认范围,这可以说是更加优雅。 使用Ninject 2.2更改默认对象作用域

第三,如果您想在每个并行操作结束时清除范围内的所有缓存对象,以避免由于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);
});

请注意,我做了一些测试,最后没有使用清晰的行,并且即使该HttpRequest完成并且我多次生成了该报告,似乎EF上下文也会重新使用。 这不是我想要的,所以清除操作很重要。 真的,我想要的行为更接近InCallScope,但试图通过InCallScope获得InRequestScope作为后备,这是我将在另一天打开的蠕虫罐。

链接地址: http://www.djcxy.com/p/50175.html

上一篇: How to do Parallel operations with Thread Scope in Web app using Ninject

下一篇: Parallel Foreach slow creation of the threads