General Electric
General Electric

Reputation: 1226

Is Ninject creating 2 separate context?

I'm creating a Web API and I'm using dependency inject wit Ninject.

I have:

IRTWRepository IModelFactory

I'm injecting those 2 into my controllers like this.

private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<IRTWRepository>().To<RTWRepository>();
            kernel.Bind<RTWAPIContext>().To<RTWAPIContext>();          
            kernel.Bind<IModelFactory>().To<ModelFactory>();
        } 

My RTWRepository constructor looks like this

 public class RTWRepository :IRTWRepository
    {
        private RTWAPIContext _context;

        public RTWRepository(RTWAPIContext context)
        {
            _context = context;
        }}

My ModelFactory constructor looks like this

public class ModelFactory : IModelFactory
    {
        private IRTWRepository _repo;        

        public ModelFactory(IRTWRepository repo)
        {
            _repo = repo;
        } 
}

I have a Controller that looks like this:

public MaterialsController(IRTWRepository repository,IModelFactory modelFactory) 
            : base(repository,modelFactory)
        {

        }

Now, my question is : Is Ninject creating 2 separate contexts when creating an instance of my RTWRepository and also when creating an instance of ModelFactory?.

The reason that I'm asking that is because I'm having a problem when I try to save an entity that has a dependency to another object which was previously retrieve from the db.

I'm saving the entity in my controller but I'm creating it in my model factory along with is dependency.

public class RecycleCenter
    {
        public RecycleCenter()
        {

        }

        public int MyProperty { get; set; }


        [Required]
        public virtual Address Address { get; set; }        
    }

The code above is for the entity Recycle Center which has an Address, this recycle center entity is created in my model factory and then in my controller I try to save it but when my repository execute this line

 _context.RecycleCenters.Add(entity);

I'm getting this error

An entity object cannot be referenced by multiple instances of IEntityChangeTracker

So, somewhere in my code I'm using 2 context instead of 1 and I think is when creating the ModelFactory and RTWRepository, is this assumption correct?, if so how do I fix it?

Upvotes: 1

Views: 81

Answers (2)

Kenneth
Kenneth

Reputation: 28737

TL;DR;

You probably need to change this line:

kernel.Bind<RTWAPIContext>().To<RTWAPIContext>();    

to

kernel.Bind<RTWAPIContext>().To<RTWAPIContext>().InRequestContext();    

Explanation:

When you define a binding in Ninject, you also specify how that object's lifecycle should be handled.

If you don't explicitly define it, Ninject's default lifecycle is Transient. Transient means that each time an instance is required, it will create a new one. In your case, you need to two instances: one for the RTWRepository of the ModelFactory and one for the RTWRepository of the MaterialsController.

You can modify the lifestyle to one of these options:

  • Singleton ==> kernel.Bind<RTWAPIContext>().To<RTWAPIContext>().InSingleTonScope();
  • Request ==> kernel.Bind<RTWAPIContext>().To<RTWAPIContext>().InRequestScope();
  • Thread ==> kernel.Bind<RTWAPIContext>().To<RTWAPIContext>().InThreadScope();
  • Named, Call, Parent, Custom

In your case, I think you need InRequestScope, but you have to check the necessary lifecycle as it depends on the application.

For further information please check out the documentation here: https://github.com/ninject/ninject/wiki/Object-Scopes

Upvotes: 1

Ivan Yonkov
Ivan Yonkov

Reputation: 7034

Most probably, it is. There's no annotation that is telling to Ninject "Hey, stop, when you have created the instance once, reuse it". You should agree that in most cases, you would want multiple instances of an object and that it is a rare case, where you want it only once.

If you want to reuse the instance, use the singleton pattern. Ninject is familiar with it, so you can bind the object mapping to a method

kernel.Bind<RTWAPIContext>().ToMethod(c => RTWAPIContext.GetInstance());

There is also a ToSingleton binding, but I bet you cannot make your context constructor private and implement C# specific singleton due to other ASP.NET problems (e.g. ASP.NET Identity will try to invoke the context's method for object creation).

Upvotes: 1

Related Questions