Here is my setup. First I am using a Unit Of Work implementation that
I got from some posts here. To be honest I don't have a deep enough
understanding of Ninject to say that it's solid, but it most
definitely seem to work. I am somewhat concerned about a potential
for memory leak, however my understanding/assumption that as long as I
use this within a "using" statement everything created by Ninject
within that objects scope should be disposed. Long story short I have
a few thousand objects which I want to process using
Parallel.Foreach. Each iteration within the parallel loop will create
an instance of several classes that inherit from the below
"SomeHelper" class. This means that each object has a fresh instance
of the IKernel. The reason I did it this way is because the entity
framework context is not thread safe (referenced below as
EFEntities). So my requirement was to make sure that each thread
operated on a fresh instance of the context. That said, I needed
multiple business layer classes to be created under the Validate()
method which would all internally share the same entity framework
object context to orchestrate a transaction so to speak.
Although the code does work as expected, and every thread gets proper
distribution of the entity framework context (I verified via
debugging) the memory usage is through the roof. The more concerning
part is that when all this code completes, I don't see the memory
being collected, so if I run it again it simply piles on top. There
might be more than one issue here, but you wanted some context :)
public class NinjectUnitOfWorkConfigModule : NinjectModule
{
public override void Load()
{
Bind(typeof(IGenericRepository<>)).To(typeof(GenericRepository<>)).InTransientScope();
//*** OTHER REPOSITORIES ***
//each using block gets a new NinjectUnitOfWork, diposed at the end
of the using block
Bind<NinjectUnitOfWork>().ToMethod(x => new
NinjectUnitOfWork(x.Kernel)).InThreadScope();
//each request gets the same ObjectContext within the
current NinjectUnitOfWork. Made available
//for GC when the using block ends
Bind<EFEntities>().ToMethod(x => new
EFEntities()).InUnitOfWorkScope();
}
}
public class NinjectUnitOfWork : ActivationBlock
{
public NinjectUnitOfWork(IResolutionRoot parent) :
base(parent)
{}
public override IRequest CreateRequest(Type service,
Func<IBindingMetadata, bool> constraint, IEnumerable<IParameter>
parameters, bool
isOptional, bool isUnique)
{
return Parent.CreateRequest(service, constraint,
parameters, isOptional, isUnique);
}
}
public static class NinjectExtensions
{
public static IBindingNamedWithOrOnSyntax<T>
InUnitOfWorkScope<T>(this IBindingWhenInNamedWithOrOnSyntax<T> self)
{
return self.InScope(x =>
self.Kernel.Get<NinjectUnitOfWork>());
}
}
public class NinjectInstance
{
public IKernel Kernel { get; private set; }
public NinjectInstance()
{
Kernel = new StandardKernel(new NinjectSettings()
{ AllowNullInjection = true } , new
NinjectUnitOfWorkConfigModule());
}
}
abstract class SomeHandler
{
//ninject instance
protected NinjectInstance ninjectInstance = new
NinjectInstance();
public virtual bool Execute()
{
LogValidationStart();
using (var unitOfWork =
ninjectInstance.Kernel.Get<NinjectUnitOfWork>())
{
Validate();
}
LogValidationEnd();
return true;
}
protected abstract bool Validate();