Autofac property injection wiring

595 views
Skip to first unread message

Kendall Bennett

unread,
Nov 30, 2013, 6:13:00 PM11/30/13
to aut...@googlegroups.com
Hi Guys,

I have noticed an anamoly in the way that Autofac handlers wiring of property injection compared to how I was used it being done in Ninject. For a lot of our stuff we use property injection because of circular references, as there is no way to resolve those without using property injection (I have tried!). So as a rule most of our primary data access, business logic and service classes use property injection, and they need to be wired up such that dependency can be circular. Now sometimes we have classes that need to be constructed on the fly, and in Ninjet we used the factory extension, but for Autofac it is conventiently built in using Func<IMyInterace> members. A lot of those classes were originally designed to only use constructor injection and relied on the interfaces injected into it being wired up when the constructor is called. However with Autofac that is NOT the case if those interfaces were wired internally with property injection.

Basically what would happen is I would have a constructor like this:

public MyClass(string arg1, int arg2, ISomeDepdency dep1, IAnothterDep dep2)
{
    // Do something with dep1 and dep2
}

But if dep1 and dep2 are set up to use property injection (in my case also using circular dependencies), dep1 and dep2 may not be actually wired up yet, so if I make calls in the constructor to dep1 or dep2 methods that internally use property injected dependencies, they will crash. This appears to happen if MyClass is the first class to consume dep1 and dep2 in the current scope, so they had to be created as part of creating the instance of MyClass. If they already existed, then it works correctly.

This is clearly a leaky abstraction layer because now MyClass has to care about HOW dep1 and dep2 were wired up, and hence I cannot do anything useful in the constructor. All I can do is save off all the parameters and then move the business logic that would have occurred in the constructor into a Load() function that is called whenever internal methods or properties are accessed.

In reality Autofac should be intelligent enough to know that it needs to COMPLETELY resolve dep1 and dep2 before passing them to MyClass. Unless there is a circular dependency to be found, this should be completely doable.

I have not yet debugged into Autofac, but I can probably build a reproduction scenario so I can then debug it. Unless one of you guys have any good ideas already about how to fix it?

- Kendall

trailmax

unread,
Dec 1, 2013, 3:22:43 PM12/1/13
to aut...@googlegroups.com
I think this is the case where tools can't be blamed. Circular dependency is quite bad and I would start from resolving that problem - eliminating the circle. Otherwise you'll have a lot of other problems.
Another problem I see is that you do work with your dependecies in constructor. One of the DI principles is that constructors should be simple and should only assign dependencies to private fields.
If you start doing things in constructor, DI container (any container) can have trouble creating the object graph. 
Once I spent days and days debugging the strange issue with MVC project. Turned out that one of sub-sub-dependencies tried to make HTTP connection in constructor. And  the resource was not available.
So exception was thrown in the constructor, hence dependencies could not be created, and the graph of objects could not be created. And I suspect this could be the case for you.

Autofac's favorite way to resolve objects is through constructor injection. For property injections and circular dependencies you need to configure container specifically: https://code.google.com/p/autofac/wiki/CircularDependencies
I never had to do property injections, never-mind circular dependencies. So can't comment on how well this works with Autofac.

Good luck with your problem and let us know what was the problem.

Cheers,
Max

Kendall Bennett

unread,
Dec 1, 2013, 8:48:49 PM12/1/13
to aut...@googlegroups.com
Well no matter whether circular dependencies are bad, sometimes you just have to use them. In my case I have tried to get rid of those dependencies but the way our code is structured it is not really possible to do it. If it was always possible to get rid of circular dependencies, then IoC containers like Ninject and Autofac simply would not support them :) Autofac does work well with resolving them, but it threw a couple of our classes for a loop that the dependencies in the constructor were not necessarily completely initialized.

I totally get that if you are resolving property injection with circular dependencies that they may not be initialized, as that is a side effect of the circular dependency problem. And you resolve circular dependencies by using property injection, and by definition you cannot rely on the properties being injected until AFTER the constructor is called. So if you mistakenly try to use a property injection field in a constructor, you will instantly crash, always. Which is a good thing. However I would fully expect that the IoC container would make sure all the dependencies ARE initialized correctly if you are using constructor injection. If that is not the case and the developer working on the code is not aware of this limitation, you can end up with some really, really subtle bugs, where in some cases things work (when the dependencies were already created earlier and just being re-used) and some cases where it crashes (if the dependencies had not yet been created).

So no, I disagree that the tool is not to blame here. IMHO it is wrong for an IoC container to NOT fully initialize all the injected dependencies before it calls the constructor where constructor injection is being used.

trailmax

unread,
Dec 2, 2013, 6:06:17 AM12/2/13
to aut...@googlegroups.com
Kendall,

In that case I'm not sure I understand the issue. 
You'll need to come up with test case that shows your issue, otherwise we can discuss this forever with no outcome.

Cheers,
Max

Alex Meyer-Gleaves

unread,
Dec 2, 2013, 7:23:02 AM12/2/13
to aut...@googlegroups.com
Hi Kendall,

I think a quick sample would certainly help to clear this up.

Cheers,

Alex.

Kendall Bennett

unread,
Dec 6, 2013, 10:46:30 PM12/6/13
to aut...@googlegroups.com
Ok, I built a small sample to show off the problem and created a defect report with it attached. Note that you CAN remove the PropertyWiringOptions.AllowCircularDependencies from the PropertiesAutowired method, and then it will resolve things correctly. But the issue here is that if you do need to use circular dependencies for some reason, things are not correctly resolved when a class that uses pure constructor injection is created until after the constructor is called.

http://code.google.com/p/autofac/issues/detail?id=477

Kendall Bennett

unread,
Dec 7, 2013, 4:48:57 PM12/7/13
to aut...@googlegroups.com
On Sunday, December 1, 2013 12:22:43 PM UTC-8, trailmax wrote:
I think this is the case where tools can't be blamed. Circular dependency is quite bad and I would start from resolving that problem - eliminating the circle. Otherwise you'll have a lot of other problems.

I just wanted to get back to this and touch on it again. If you seriously think that circular dependencies are bad, and that you can just remove them from your code, then I would suspect you have not worked on very many large scale projects. Just for laughs I went back to our code to take another look at whether circular dependencies can easily be removed or not. They cannot.

It is also worth pointing out that circular dependencies via property injection are actually a great way to structure your code because it allows any class to depend upon some other interface, without having to care anything about how that interface is implemented. If the class that implements the interface depends on some other interface, and then it depends on another, and perhaps that other one refers back to the original class, you have a circular dependency that is very difficult to remove, but more importantly is a very clean way to implement your code. You could actually write your code using constructor injection and it would compile just fine, but it would crash at runtime with a circular dependency exception. To fix it you have to force your code to CARE about whether some interface it depends upon also depends upon some other interface and re-factor the code accordingly, which IMHO is an anit-pattern, and a leaky abstraction. The whole point of IoC and using interfaces is to remove the coupling between the classes, and allowing circular dependencies does this extremely cleanly.

By definition requiring constructor injection means that the object graph of your classes in memory has to be a single hierarchy, which does not fit at all well with most real world scenarios. In most of the case in our code where we have circular dependencies they are not removable because they are at quite a deep level, not from one class to another.

So no, I think it is completely wrong to preach that circular dependencies are bad, and should be avoided at all costs.

Kendall Bennett

unread,
Dec 7, 2013, 5:57:55 PM12/7/13
to aut...@googlegroups.com
Fix is posted on the bug report. I would love to get this built and officially pushed to NuGet ...
Reply all
Reply to author
Forward
0 new messages