This project is read-only.

Circular Reference

Feb 21, 2015 at 12:16 AM
I have a ClassA and a ClassB. Each has a dependency on each other. When I try to wire them up and call Verify(), it fails. With constructor injection I understand why this couldn't be possible, but I tried it with explicit autowiring using Autowire attributes, and it still failed. I get the following stack trace:

System.InvalidOperationException was unhandled
HResult=-2146233079
Message=The configuration is invalid. Creating the instance for type Class1 failed. The registered delegate for type Class1 threw an exception. The registered delegate for type Class2 threw an exception. The configuration is invalid. The type Class1 is directly or indirectly depending on itself.
Source=SimpleInjector
StackTrace:
   at SimpleInjector.InstanceProducer.VerifyExpressionBuilding()
   at SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt(InstanceProducer[] producersToVerify)
   at SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt()
   at SimpleInjector.Container.Verify()
And the inner exception is an ActivationException.

Is there any way I can do this circular referencing?
Feb 21, 2015 at 7:08 AM
What do you mean by 'autowire attributes'? Can you show an example of your configuration, show the two classes in question and show how you would solve this problem when you would create these instances manually?
Feb 22, 2015 at 12:44 AM
Edited Feb 22, 2015 at 12:46 AM
AUtowire Attributes - In the documentation, one of the extension points is about how to introduce your own auto-injection mechanism, and in the code examples they show how to do this with Autowire attributes. Basically, you just mark a property with the attribute, override the default "PropertySelectionBehavior" option of the Container, and then you have automatic injection of properties.

The 2 classes would be something very much like this:
public class ServiceA : IServiceA
{
    [Autowire]
    public ServiceB serviceB { get; set; }
}

public class ServiceB : IServiceB
{
    [Autowire]
    public ServiceA serviceA { get; set; }
}

Container container = new Container();
container.Options.PropertySelectionBehavior = new AutowirePropertySelectionBehaviour();
container.Register<IServiceA,ServiceA>();
container.Register<IServiceB,ServiceB>();
Manually, this would be simply:
ServiceA a = new ServiceA();
ServiceB b = new ServiceB();

a.serviceB = b;
b.serviceA = a;
That is what throwing the exception. Again, this also happens with the default Constructor injection, although I can see why it would happen there.

I'm trying to do this because in my architecture, in the service layer all services should be able to "see" each other in order to leverage everyone's functionality.
Feb 22, 2015 at 10:17 AM
Edited Feb 22, 2015 at 10:20 AM
Your manual example does make this look very easy but it also highlights a fragility that Simple Injector exposes and does not naturally support.

There is a moment when your services are constructed but are not safe to use: you have a two stage process to complete the activation of your object graph.

Stage 1:
ServiceA a = new ServiceA();
ServiceB b = new ServiceB();
(At this point the objects are constructed but are not safe to use.)

Stage 2:
a.serviceB = b;
b.serviceA = a;
There are multiple ways to resolve your basic example.
ServiceA a = new ServiceA();
ServiceB b = new ServiceB();
a.serviceB = b;
b.serviceA = a;
ServiceB b = new ServiceB();
ServiceA a = new ServiceA();
a.serviceB = b;
b.serviceA = a;
ServiceA a = new ServiceA();
ServiceB b = new ServiceB();
b.serviceA = a;
a.serviceB = b;
ServiceB b = new ServiceB();
ServiceA a = new ServiceA();
b.serviceA = a;
a.serviceB = b;
The container cannot Compose object graphs with confidence because of the circular references and it won't have a go and hope for the best.

There may be a way to intercept the activation pipeline and inject the properties yourself but I'm not sure of how, I suggest you reconsider your design.

Please also be aware of this point made in the same documentation you referred to earlier:
... the use of property injection should be very exceptional and in general constructor injection should be used in the majority of cases. If a constructor gets too many parameters (constructor over-injection anti-pattern), it is an indication of a violation of the Single Responsibility Principle (SRP). SRP violations often lead to maintainability issues. So instead of patching constructor over-injection with property injection, the root cause should be analyzed and the type should be refactored, probably with Facade Services ...
Feb 22, 2015 at 1:45 PM
Property injection in Simple Injector is done directly after the type as been constructed. This means that it is impossible to use auto-wired properties to build cyclic dependency graphs. Technically this is possible, and I even think that some other containers do actually attempt to do this in some cases. Cyclic dependency graphs however are in general a design smell and this is the main reason why Simple Injector does not attempt to resolve cyclic dependency graph. You already see that objects are harder to construct (even without a container) and Qujck correctly refers to Temporal Coupling which is a design smell. It's much better to be able to safely construct an object graph in one single pass.

How to actually refactor away from such cyclic dependency highly depends upon the actual design, but the general solution is to extract the coupled part to its own service that can both use ServiceA and ServiceB:
Orchestrator c = new Orchestrator();
ServiceB b = new ServiceB(c);
ServiceA a = new ServiceA(c);
Feb 22, 2015 at 3:09 PM
Edited Feb 23, 2015 at 11:58 AM
Take a look at this Stackoverflow q/a that shows a way to work around this.
Feb 22, 2015 at 6:32 PM
Thank you both for your responses. I was already already considering both a Facade and the use of RegisterInitializer as isolated workarounds to my issue, but it seems now that the best approach would be to promote those workarounds to full design features instead of just here and there, and try to stick with only one for the sake of consistency. Of course, I will also reconsider the original design.

Thanks!