Composed Relationship Types - Func<Owned<T>>

We often end up in situations where injecting a service using simple relationship types like Func<T>, Lazy<T>, 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 runtime. Now, for such a scenario, injecting the service as either Func<T> or Owned<T> alone is not enough. This, in fact, only solves half of the problem and this is when composed relationship types come into the 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 types when we require:
  • a factory that returns lifetime-controlled ILogger service - Func<Owned<ILogger>> 
  • all implementations of factories that return ILogger services - IEnumerable<Func<ILogger>>
  • all implementations of factories that return lifetime-controlled ILogger services - IEnumerable<Func<Owned<ILogger>>>
  • all implementations of ILogger service and use lazy initialization based on some additional data(metadata) - IEnumerable<Lazy<ILogger, LoggerMetadata>> or sometimes the longer form as IEnumerable<Meta<Lazy<ILogger>,LoggerMetadata>>
Not to mention, this is a fairly a short list and there can be many more scenarios where we need to compose a relationship type. However, going further we will be talking about the most commonly used composed relationship Func<Owned<T>>.

Using Func<Owned<T>>
 
Consider a SwitchUserView class (say, windows form) that accepts a dependency of IPrintView to render some sort of unique view to its use as requested. Now, there are two important things:
  • The user never requests 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 runtime, and therefore we need a Func<T>.

  • The view object is held for too long
    Think of a scenario when the user closes the IPrintView and 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 Owned<T>.
Satisfying either of the problems is not enough. Therefore, we need a composed relationship type Func<Owned<IPrintView>>, which solves both of our problems. The code below shows the implementation:
  1. public interface IPrintView  
  2. {  
  3.     void Show();  
  4. }  
  5.   
  6. public class SwitchUserView  
  7. {  
  8.     private Func<Owned<IPrintView>> _printViewCreator;  
  9.     public SwitchUserView(Func<Owned<IPrintView>> printViewCreator)  
  10.     {  
  11.         _printViewCreator = printViewCreator;  
  12.     }  
  13.   
  14.     public void SwitchToPrintView()  
  15.     {  
  16.         using(var printView = _printViewCreator())  
  17.         {  
  18.             printView.Value.Show();  
  19.         }  
  20.     }  
  21. }  
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.
  1. var builder = new ContainerBuilder();      
  2.   
  3. builder.RegisterType<SwitchUserView>().AsSelf();  
  4. builder.RegisterType<PrintView>().As<IPrintView>();       
  5.   
  6. // Register other components   
Summary
 
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.

Related Articles