We're using Ninject dependency injection for a machine automation software and have run into a problem with circular references. We have several physical objects that needs to be aware of each other to avoid collisions. We have added a CollisionHandler class that authorizes each movement. For example: The robot arm checks that the hatch is open before moving through it. And the hatch checks that the robot arm is out of the way before closing.
I performed a proof of concept test of the Ninject setter method injection pattern with just the robot and the collision handler. That worked fine, first the Robot was created, then the CollisionHandler and finally the inject method was called on the Robot. Both Robot, Hatch and CollisionHandler are bound as singletons:
Bind<Robot>().ToSelf().InSingletonScope()
Bind<Hatch>().ToSelf().InSingletonScope()
Bind<CollisionHandler>().ToSelf().InSingletonScope()
public class Robot
{
private CollisionHandler _collisionHandler;
public Robot(ISomeService someService)
{
}
[Inject]
public void PostConstructInject(CollisionHandler collisionHandler)
{
if (_collisionHandler != null)
throw new InvalidOperationException("PostConstructInject called more than once");
_collisionHandler = collisionHandler;
}
}
public class CollisionHandler
{
private readonly Robot _robot;
private readonly Hatch _hatch;
public CollisionHandler(Robot robot, Hatch hatch)
{
_robot = robot;
_hatch = hatch;
}
public bool IsRobotAwayFromHatch() { }
public bool IsHatchOpen() { }
}
It all seemed fine so I moved on to implementing the pattern for the other entities. That's where it stopped working. Adding the inject method to the hatch, Ninject is no longer able to construct the object graph:
public class Hatch
{
private CollisionHandler _collisionHandler;
public Hatch(ISomeOtherService someOtherService)
{
}
[Inject]
public void PostConstructInject(CollisionHandler collisionHandler)
{
if (_collisionHandler != null)
throw new InvalidOperationException("PostConstructInject called more than once");
_collisionHandler = collisionHandler;
}
}
The problem is that Ninject wants to call the inject method directly after constructing the object:
Activation path:
4) Injection of dependency CollisionHandler into parameter collisionHandler of method PostConstructInjection of type Robot
3) Injection of dependency Robot into parameter robot of constructor of type CollisionHandler
2) Injection of dependency CollisionHandler into parameter collisionHandler of method PostConstructInjection of type Hatch
1) Request for Hatch
This equals the following code:
Robot robot = new Robot(someService);
robot.PostConstructInject(/* We need a CollisionHandler instance before it is constructed */);
Hatch hatch = new Hatch(someOtherService);
CollisionHandler collisionHandler = new CollisionHandler(robot, hatch);
hatch.PostConstructInject(collisionHandler);
What I would like it to do is to move the PostConstructInject calls to after the CollisionHandler instance has been created:
Robot robot = new Robot(someService);
Hatch hatch = new Hatch(someOtherService);
CollisionHandler collisionHandler = new CollisionHandler(robot, hatch);
robot.PostConstructInject(collisionHandler);
hatch.PostConstructInject(collisionHandler);
Is there some way I can tell Ninject to hold off calling the injection method until it is possible? I could remove the [Inject] attribute and have the CollisionHandler call those methods but that feels quite ugly.