C# Ninject——如何使用Parallel.ForEach循环中使用的类创建IOC

C# Ninject——如何使用Parallel.ForEach循环中使用的类创建IOC,c#,inversion-of-control,ninject,C#,Inversion Of Control,Ninject,我有一些代码如下所示。我是IoC和Ninject的新手,不知道如何将IOtherClass注入到某个服务中,以便Parallel.ForEach工作。我唯一的另一个猜测是将Parallel.ForEach移到DoWork方法中 谢谢你的帮助 public class SomeService { Parallel.Foreach(items, item => new OtherClass.DoWork(item); } public c

我有一些代码如下所示。我是IoC和Ninject的新手,不知道如何将IOtherClass注入到某个服务中,以便Parallel.ForEach工作。我唯一的另一个猜测是将Parallel.ForEach移到DoWork方法中

谢谢你的帮助

public class SomeService
{
    Parallel.Foreach(items, item =>
                       new OtherClass.DoWork(item);
}

public class OtherClass:IOtherClass
{
    public void DoWork()
    {...}
}

如果在循环的每次迭代中都需要创建
OtherClass
的新实例,那么从代码中看不出这一点?我假设IOtherClass接口包含
DoWork
方法? 如果您可以在每次迭代中使用相同的实例,那么正确的方法就是构造函数注入

SomeService
的构造函数中,注入
IOtherClass

public class SomeService
{
    private readonly IOtherClass _otherClass;
    public SomeService(IOtherClass otherClass)
    {
        _otherClass = otherClass;
    }

    void YourLoopMethod()
    {
       Parallel.Foreach(items, item =>_otherClass.DoWork(item));
    }
 }
如果您在循环的每次迭代中都需要新实例,那么您可以使用注入
SomeService

public interface IOtherClassFactory
{
    IOtherClass Create();
}

public class SomeService
{
    private readonly IOtherClassFactory _otherClassFactory;
    public SomeService(IOtherClassFactory otherClassFactory)
    {
        _otherClassFactory = otherClassFactory;
    }

    void YourLoopMethod()
    {
       Parallel.Foreach(items, item =>
                   _otherClassFactory.Create().DoWork(item));
    }
 }
在这里,您需要实现知道如何创建
IOtherClass
对象的
IOtherClassFactory
。在您的实现中,您可以注入
IKernel
依赖项,并使用它创建
IOtherClass
对象

对于这两种情况,您都需要在组合根目录中注册Ninject内核绑定

   kernel.Bind<IOtherClas>().To<OtherClass>();
kernel.Bind().To();

应用依赖项注入时,您希望将相关对象的图形组合控件移动到应用程序中称为的单个位置

要消除应用程序其余部分的复杂性,只有合成根用户应该知道要创建哪些实例以及如何创建它们。组合根是唯一知道对象图是如何构建的,并且它知道(或者至少在编写DI配置时,您应该知道)服务是否可以安全地相互依赖的根。这里应该指出线程安全性的问题

实现本身不必知道使用依赖关系是否安全;它必须能够假设依赖关系在它正在运行的线程上使用是安全的。除了通常的瞬态和单例生活方式(其中瞬态意味着创建一个新实例,而单例意味着应用程序将只有该服务的一个实例)之外,通常还有其他具有线程亲和力的生活方式。以每个Web请求的生活方式为例

因此,一般的建议是:

  • 让IoC容器为您解析所有对象,然后
  • 不要将服务从一个线程移动到另一个线程
从线程安全的角度来看,您的
SomeService
实现是正确的,因为每个线程都创建自己的新(瞬态)
OtherClass
实例。但是当
OtherClass
开始独立依赖时,这就开始成为问题。在这种情况下,您应该将该对象的创建移动到合成根。但是,如果按照以下示例中的方式实现,则会出现问题:

public class SomeService
{
    private IOtherClass other;
    public SomeService(IOtherClass other)
    {
        this.other = other;
    }

    public void Operate(Items items)
    {   
        Parallel.Foreach(items, item => this.other.DoWork(item));
    }
}
实现是有问题的,因为
SomeService
跨线程移动单个
IOtherClass
实例,并假设
IOtherClass
是线程安全的,这是只有组合根应该知道的知识。在整个应用程序中分散这些知识会增加复杂性

在处理多线程代码时,每个线程都应该有自己的对象图。这意味着当启动一个新线程时,您应该再次查询容器/内核中的根对象并调用该对象。像这样:

public class SomeService
{
    private IItemProcessor processor;

    public SomeService(IItemProcessor processor)
    {
        this.processor = processor;
    }

    public void Operate(Items items)
    {
        this.processor.Process(items);
    }
}

public class ItemProcessor : IItemProcessor
{
    private IKernel container;

    public ItemProcessor(IKernel container)
    {
        this.container = container;
    }

    public void Process(Items items)
    {
        Parallel.Foreach(items, item =>
        {
            // request a new IOtherClass again on each thread.          
            var other = this.container.Get<IOtherClass>();
            other.DoWork(item);
        });    
    }
}
公共类服务
{
专用IItemProcessor处理器;
公共服务(IItemProcessor处理器)
{
this.processor=处理器;
}
公共无效操作(项目)
{
本.处理器.过程(项目);
}
}
公共类ItemProcessor:IITempProcessor
{
私人IKernel集装箱;
公共项目处理器(IKernel容器)
{
this.container=容器;
}
公共作废流程(项目)
{
Parallel.Foreach(项目,项目=>
{
//在每个线程上再次请求新的IOtherClass。
var other=this.container.Get();
其他.嫁妆(项目);
});    
}
}
这有意地将处理这些项目的机制提取到不同的类中。这允许将业务逻辑保留在
SomeService
中,并允许将
ItemProcessor
(应仅包含机制)移动到组合根目录中,以防止使用


解释有关在多线程应用程序中使用DI的更多信息。请注意,它是针对不同框架的文档,但一般建议是相同的。

很抱歉,尽管您的回答是务实的,但我不同意您的建议。注入
IOtherClass
IOtherClassFactory
都会迫使
SomeService
假设这些依赖关系是线程安全的,实现不需要关心这些知识。@Steven您的回答很好,我同意您所说的一切。但在IOtherClassFactory的第二个场景中,我提出了类似的解决方案,即为IKernel注入工厂的每个并行迭代创建新的对象图。您的解决方案是假设IKernel是线程安全的。。最后,一切都归结为组件文档。有人可能会将其他一些IKernel实现注入到您的ItemProcessor中,然后您就跟我一样了。我同意使用工厂是一个灰色地带,因为它们通常是单例的,因此线程安全。Stll这是一个假设,信息正在从您的合成根中泄漏出来。我的解决方案假设您的DI容器是线程安全的,这是事实。但是在维护组合根目录时,您必须了解DI框架;没有其他选择(除了不使用di框架)。但是这种编程方式的优点是