Reputation: 6611
I realise that DI is a very flexible design pattern, although I'm struggling to accept it as my 'silver bullet' for creating decoupled code.
Here's why: What happens when the dependent object has a longer lifetime than the dependencies it has been injected with?
Example application: I have a BusinessLogic
class which is instantiated for the lifetime of my application. This class requires a DataContext
object to perform database operations. I have thefore created an abstract DataContextFactory
with two implementations: StaticDataContextFactory
and WebDataContextFactory
. The former maintains a single DataContext for the lifetime of the application, whereas the latter will create new DataContexts for each HTTP request.
Problem in the example : As you can see, all will be fine when the StaticDataContextFactory
is used. However, when the WebDataContextFactory
is used the BusinessLogic
will fail, since it's injected with a DataContext
which will expire/dispose once the first request completes.
My question is: Must all dependent objects have a lifetime which is less or equal to the lifetime of its dependencies? If so, then what happens when the lifetime of each dependency is unknown to the code which instantiates the dependent classes?
Upvotes: 3
Views: 2208
Reputation: 31857
As other posters have pointed out, there are proxy-based solutions to this. I'd put that in the 'last resort' category though.
You can refactor to remove this inconsistency, and I think the end result will be nicer to work with in the long run. I don't know a lot about your scenario, but a few things you could consider:
Get rid of the factories, let the container inject the DataContext
and then use the container's lifetime control to adjust the lifetime of the DataContext
in different environments
Don't make the BusinessLogic
component single-instance. If you create a new one for each use, it will naturally pick up a web-scoped DataContext
if DC is configured that way, or the single DC in the other configuration
If BusinessLogic
has state or is expensive to instantiate, move the expensive/stateful parts into sub-components that have single-instance lifetime
I've seen the proxy-based solution that can be used in Spring - it is personal taste but I'd be wary about how understandable this solution will be long-term. You'd have to be very disciplined to make sure that anything returned from the 'current web request' through the proxy would not be referenced or kept around longer than the request that owns it...
Working successfully with lifetime in IoC really relies heavily on keeping a clean separation between units of work, which in a web environment is pleasant and natural - it will pay to go with the flow if you can.
Hope this helps, Nick
Upvotes: 1
Reputation: 6150
The Spring framework's web integration addresses this problem using proxies and aspects. Longer-scoped objects are injected with proxies to the shorter-scoped objects. Each proxy knows how to fetch the "current" version of its shorter-scoped delegate, via the HTTP session or HTTP request (for session- and request-scoped beans, respectively).
Upvotes: 1
Reputation: 382
Why can't you ask the XXXDataContextFactory for the "Current" DataContext? Your Static factory will always return the same one, while your Web factory will return one based on the current HttpRequest.
Upvotes: 0
Reputation: 10015
I have been pondering a similar question. (Check my post on DI: Dependency Injection Container, Second Answer) I realized that a proper implementation using Interface Injection would actually do the trick.
The reason I think it may work well is because while it is common to extract an interface from an object, as long as the interface hierarchies are properly designed, even if the objects go out of scope, the data relevant to the interface will actually persist. Therefore you would not end up having a dependency that goes out of scope before your actual object has been released by the code that consumes it.
In the example I've shown I rolled out my own "DI" although it is not really a true DI pattern, i believe it does the job pretty well.
Upvotes: 0