Ninject exception in Parallel.Foreach
Asked Answered
T

2

7

I have a piece of code that runs a Parallel.Foreach on a list of items to process. Each iteration creates a couple of objects with each object instantiating and disposing it's own instance of the Ninject IKernel. The IKernel is disposed when the object is done it's work.

That said, this code works perfectly well on my Windows 7, I7 laptop. However when I push it out to my VPS that runs Windows 2008 I get this exception. The exception doesn't happen on the same iteration, sometimes it will get through 10 iterations and throw an exception, other times it will go through hundreds of them. Obviously seems like a threading issue, but it doesn't happen anywhere but my VPS. If it matters this is being hosted in 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)

Here is a snippet of the code:

//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();
        }
    }
}   

Edit 1 One thing is for sure, this is a thread safety issue and I should not be creating more than one instance of IKernel per application. It's a matter of understanding on how to configure the proper scopes in order to accomplish Entity Framework Context thread safety yet preserving the UoW type approach where multiple business layer classes can share the same EF context within the UoW scope within a single thread.

Tartaric answered 28/4, 2011 at 14:53 Comment(0)
I
5

See http://groups.google.com/group/ninject/browse_thread/thread/574cd317d609e764

As I told you Ninject's ctor is not threadsafe atm unless you are using NOWEB! If creating/disposing the kernel so many times you will have to synchronize the access yourself! I still suggest to redesign your UoW implementation!

Indue answered 29/4, 2011 at 0:12 Comment(1)
Thanks Remo, working with you on the google users group on this. I will update here with the correct answer as soon as I understand what it is.Tartaric
T
-1

It seems like ninjectInstance is an instance variable. Hence, it's possible that in a parallel environment, ninjectInstance.Dispose() will be called twice (calling Kernel.Dispose() does not set the Kernel property to null) for the same instance and since Kernel.Dispose() is already been called, the method fails.

Maybe you wanted something like

using (var ninjectInstance = new NinjectInstance()) {
..
}
Tommi answered 28/4, 2011 at 15:20 Comment(5)
I don't think that's a possibility as only one thread (the one that handles the particular Parallel.Foreach iteration) creates the object that has a class level ninjectInstance, which is then used within the "using" statement. As soon as the next iteration takes place that object is already out of scope, the next iteration creates its own set. The reason it's an instance variable and class scoped is because it allows classes inheriting from this object to use base.ninjectInstance.Get<> easily within the Init() and Validate() methods. Without me having to pass it as a parameter.Tartaric
BTW just to be safe I set the Kernel = null, right after Kernel.Dispose(), same problem.Tartaric
-1 While you're only trying to be helpful, this has nothing to do with the underlying problem and does not add anything factual to the debate, sorry...Berners
@Ruben, why don't you educate us what is the underlying problem, and why was my answer "nothing to do with it"?Tommi
@Remo Gloor has already taken care of it - the code that the OP posted is fine, and correctly uses the Dispose pattern, even if there may be minor style things. The problem is a relatively surprising lack of thread safety in the ninject implementation. To suggest that rejigging as you're suggesting is going to make a material difference is incorrect. There's no need for me to educate anyone, just point stuff that's wrong so people who might not be familiar with the details will not confuse speculation with fact. Re-reading I see this is an overreaction to your question, but....Berners

© 2022 - 2024 — McMap. All rights reserved.