Исключение Ninject параллельно.Для каждого

#c# #.net #asp.net #ninject #ninject-2

#c# #.net #asp.net #ninject #ninject-2

Вопрос:

У меня есть фрагмент кода, который выполняет Parallel.Foreach список элементов для обработки. Каждая итерация создает пару объектов, причем каждый объект создает свой собственный экземпляр Ninject IKernel и распоряжается им. IKernel удаляется, когда объект выполняет свою работу.

Тем не менее, этот код отлично работает на моем ноутбуке с Windows 7, I7. Однако, когда я отправляю его на свой VPS, который работает под управлением Windows 2008, я получаю это исключение. Исключение не возникает на одной и той же итерации, иногда оно проходит через 10 итераций и выдает исключение, в других случаях оно проходит через сотни из них. Очевидно, что это похоже на проблему с потоками, но это не происходит нигде, кроме моего VPS. Если это имеет значение, это размещается в ASP.NET IIS.

 System.AggregateException: One or more errors occurred. --->
System.ArgumentOutOfRangeException: Index was out of range. 
    Must be non-negative and less than the size of the collection.  
    Parameter name: index
        at System.Collections.Generic.List`1.RemoveAt(Int32 index)
        at Ninject.KernelBase.Dispose(Boolean disposing)
  

Вот фрагмент кода:

 //Code that creates and disposes the Ninject kernel
using(ninjectInstance = new NinjectInstance())
{
    using (var unitOfWork = ninjectInstance.Kernel.Get<NinjectUnitOfWork>())
    {
        Init();
        continueValidation = Validate(tran, ofr);
    }
}

public class NinjectInstance : IDisposable
{
    public IKernel Kernel { get; private set; }

    public NinjectInstance() 
    {           
        Kernel = new StandardKernel(
           new NinjectSettings() { AllowNullInjection = true }, 
           new NinjectUnitOfWorkConfigModule());          
    }

    public void Dispose()
    {
        if (Kernel != null)
        {
            Kernel.Dispose();
        }
    }
}   
  

Правка 1
Одно можно сказать наверняка, это проблема потокобезопасности, и я не должен создавать более одного экземпляра IKernel для каждого приложения. Это вопрос понимания того, как настроить надлежащие области для обеспечения потокобезопасности контекста Entity Framework, сохраняя при этом подход типа UoW, при котором несколько классов бизнес-уровня могут совместно использовать один и тот же контекст EF в области UoW в рамках одного потока.

Ответ №1:

Смотрите http://groups.google.com/group/ninject/browse_thread/thread/574cd317d609e764

Как я уже говорил вам, ctor Ninject не является потокобезопасным atm, если вы не используете NOWEB ! Если вы создаете / удаляете ядро так много раз, вам придется синхронизировать доступ самостоятельно! Я все еще предлагаю переработать вашу реализацию UoW!

Комментарии:

1. Спасибо, Римо, за работу с вами в группе пользователей Google над этим. Я обновлю здесь правильный ответ, как только пойму, что это такое.

Ответ №2:

Похоже, что ninjectInstance это переменная экземпляра. Следовательно, возможно, что в параллельной среде, ninjectInstance.Dispose() будет вызван дважды (вызов Kernel.Dispose() не устанавливает для свойства Ядра значение null) для одного и того же экземпляра, и поскольку Kernel.Dispose() он уже был вызван, метод завершается с ошибкой.

Может быть, вы хотели что-то вроде

 using (var ninjectInstance = new NinjectInstance()) {
..
}
  

Комментарии:

1. Я не думаю, что это возможно, поскольку только один поток (тот, который обрабатывает конкретную параллель. На каждой итерации) создается объект, имеющий ninjectInstance уровня класса, который затем используется в инструкции «using». Как только выполняется следующая итерация, этот объект уже выходит за пределы области видимости, следующая итерация создает свой собственный набор. Причина, по которой это переменная экземпляра и область действия класса, заключается в том, что она позволяет классам, наследуемым от этого объекта, легко использовать base.ninjectInstance.Get<> в методах Init() и Validate() . Без необходимости передавать его в качестве параметра.

2. Кстати, на всякий случай я установил Kernel = null сразу после Kernel. Dispose(), та же проблема.

3. -1 Хотя вы всего лишь пытаетесь быть полезными, это не имеет ничего общего с основной проблемой и не добавляет ничего фактического к дискуссии, извините…

4. @Ruben, почему бы вам не рассказать нам, в чем заключается основная проблема, и почему мой ответ был «не имеет к этому никакого отношения»?

5. @Remo Gloor уже позаботился об этом — код, опубликованный OP, в порядке и корректно использует шаблон Dispose, даже если могут быть незначительные изменения стиля. Проблема заключается в относительно неожиданном отсутствии потокобезопасности в реализации ninject. Предполагать, что перенастройка, как вы предлагаете, будет иметь существенное значение, неверно . Мне нет необходимости кого-либо просвещать, просто указывайте на неправильные вещи, чтобы люди, которые могут быть не знакомы с деталями, не путали предположения с фактом. Перечитывая, я вижу, что это чрезмерная реакция на ваш вопрос, но….