We often end up in situations where injecting a service using simple relationship types like
Owned<T> and others, doesn’t serve the purpose. For instance, there can be a scenario where we want to control the lifetime of a component and yet the initialization of the component takes place at run time. Now, for such a scenario, injecting the service as either
Owned<T> alone is not enough. This, in fact, only solves half of the problem and this is when composed relationship types come into picture.
Composed Relationship Types
It’s the beauty of relationship types, that they can work in conjunction with one another. Therefore, we can compose relationship types to solve a particular problem. For instance, use composed relationship type when we require:
- a factory that returns lifetime-controlled
- all implementations of factories that return
- all implementations of factories that return lifetime-controlled
- all implementations of
ILoggerservice and use lazy initialization based on some additional data(metadata) –
IEnumerable<Lazy<ILogger, LoggerMetadata>>or sometimes the longer form as
Not to mention, this is fairly a short list and there can be many more scenarios where we require to compose a relationship type. However, going further we will be talking about the most commonly used composed relationship
SwitchUserView class (say, windows form) that accepts a dependency of
IPrintView to render some sort of unique view to its user as requested. Now, there are two most important things:
- User never requests for the view: In such a case, injecting the view as a direct dependency is not an option. In fact, the decision is to be made at run time, and therefore we need a
- The view object is held for too long: Think of a scenario when user closes the
IPrintViewand stays on the parent form, trying other available views. The point is, if the view is resolved in the same lifetime scope as that of its parent form, the view object might be held for too long in the memory, before it is released. And, so will be its dependencies, if any. Therefore we need to control the lifetime of the view using
Satisfying either of the problems is not enough. Therefore, we need a composed relationship type
Func<Owned<PrintView>>, which solves both of our problems. The code below shows the implementation:
public interface IPrintView
public class SwitchUserView
private Func<Owned<IPrintView>> _printViewCreator;
public SwitchUserView(Func<Owned<IPrintView>> printViewCreator)
_printViewCreator = printViewCreator;
public void SwitchToPrintView()
using(var printView = _printViewCreator())
It’s not always the case that we need to compose relationship types, but yes they are used quite enough.
Register the Components
The registration of the components with the container will be no different than what we have seen in earlier posts.
var builder = new ContainerBuilder();
// Register other components
While Func<Owned<T>> might not suit everyone’s requirement, the whole intention here is to see how we can compose different relations that best suit our situation. I hope this article helps one and all to get a basic understanding of the topic, yet makes you confident enough. All you need is to come up with different scenarios and see which relation would be best, or if you don’t need to compose a relationship at all.