2013-05-31 3 views
2

У меня есть код, который выглядит ниже. Я новичок в IoC и Ninject и не знаю, как вводить IOtherClass в SomeService, чтобы Parallel.ForEach работал. Мое другое предположение - переместить Parallel.ForEach в метод DoWork?Ninject - как к IOC с классом, используемым в цикле Parallel.ForEach

Спасибо за любую помощь.

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

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

ответ

5

При применении инъекции зависимостей, вы хотите, чтобы переместить контроль состава графиков соответствующего объекта в одном месте в приложении под названием Composition Root.

Чтобы удалить сложность в остальной части приложения, только созданный корень должен знать, какие экземпляры создавать и как их создавать. Корень состава - единственный, кто знает, как строятся графические объекты, и он знает (или, по крайней мере, при написании конфигурации DI, которую вы должны знать), если службы могут безопасно зависеть друг от друга. Здесь следует определить проблемы безопасности потока.

Сама реализация не должна знать, безопасно ли использовать зависимость; он должен уметь предположить, что эта зависимость безопасна для использования в потоке, в котором она запущена. Помимо обычного временного и одноэлементного образа жизни (где переходный процесс означает, что создается новый экземпляр, где singleton означает, что приложение будет иметь только один экземпляр этой службы), часто бывают другие образы жизни, которые имеют сходство потоков. Возьмем, к примеру, образ жизни в Интернете.

Так общий совет заключается в следующем:

  • Пусть контейнер 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); 
     });  
    } 
} 

Это намеренно извлекает механику обработки этих предметов в другой класс. Это позволяет сохранить бизнес-логику в SomeService и позволяет перемещать ItemProcessor (который должен содержать только механики) в корень композиции, поэтому предотвращается использование Service Locator anti-pattern.

This article объясняет больше о работе с DI в многопоточных приложениях. Обратите внимание, что это документация для другой структуры, но общие рекомендации одинаковы.

+0

Благодарим вас за отличное объяснение и пример. Я смог понять и реализовать! – NBPC77

+0

Инъекция IKernel в ItemProcessor не является хорошей идеей, потому что бизнес-класс знает о Ninject. Лучший способ - ввести делегата, например Func . – mtkachenko

+1

@mtkachenko: Как я объяснил в своем ответе, «ItemProcessor» должен быть частью корня композиции, что означает, что он * не * бизнес-компонент больше, а инфраструктурный компонент. – Steven

0

Это не очевидно из кода, если вам необходимо создать новый экземпляр 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)); 
    } 
} 

Здесь вам необходимо реализовать IOtherClassFactory, который знает, как создать объекты IOtherClass. В своей реализации вы можете ввести зависимость IKernel и использовать ее для создания объектов IOtherClass.

И для обоих сценариев вам необходимо зарегистрировать привязки ядра Ninject в вашем корне композиции.

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

Прошу прощения, хотя ваш ответ прагматичен, я не согласен с вашим советом. Оба ввода «IOtherClass» и «IOtherClassFactory» заставляют «SomeService» предполагать, что эти зависимости являются потокобезопасными, знание о том, что реализациям не нужно заботиться. – Steven

+0

@Steven Хорошо, что вы хорошо отвечаете, я согласен со всем, что вы сказали. Но по моему второму сценарию с IOtherClassFactory я предложил подобное решение, которое создает новый график объектов для каждого Parallel для итерации с IKernel, который вводится в заводскую. В вашем решении предполагается, что IKernel является потокобезопасным. В конце все сводится к документации по компонентам. Возможно, кто-то может ввести некоторые другие реализации IKernel в ваш ItemProcessor, а затем вы находитесь в одной лодке, как я ее вижу. – jure

+0

Я согласен с тем, что использование фабрики - это серая область, поскольку они часто являются одноточечными и, следовательно, потокобезопасными. Stll ths - это предположение, и информация утечки из вашего корня композиции. Мое решение предполагает, что ваш контейнер DI является потокобезопасным, это правда. Но вы должны знать свою инфраструктуру DI при сохранении своего корня композиции; нет альтернативы (кроме использования рамки di). Но преимущество этого способа программирования в том, что остальная часть приложения не должна знать. – Steven

Смежные вопросы