【问题标题】:How to do Parallel operations with Thread Scope in Web app using Ninject如何使用 Ninject 在 Web 应用程序中使用 Thread Scope 进行并行操作
【发布时间】:2017-10-06 00:40:51
【问题描述】:

每当使用新的 DI 框架时,我都会反复遇到同样的问题……如何运行从 HttpRequest 开始的大规模并行操作,其中每个线程都需要自己唯一的依赖项副本?就我而言,我使用的是 Ninject。

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

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

请注意,此报告足够大,可以保证 Parallel.ForEach,但也足够小,可以在 Web 请求上同步运行,并且不会使浏览器超时(很多。

【问题讨论】:

    标签: dependency-injection task-parallel-library ninject


    【解决方案1】:

    该解决方案有几个不同的移动部件,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 是我将在另一天打开的一罐蠕虫。

    【讨论】:

      猜你喜欢
      • 2014-05-22
      • 2017-05-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-07
      相关资源
      最近更新 更多