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 run time. 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 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 ILogger service –  Func<Owned<ILogger>>
  • all implementations of factories that return ILogger servicesIEnumerable<Func<ILogger>>
  • all implementations of factories that return lifetime-controlled ILogger servicesIEnumerable<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 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 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 user as requested. Now, there are two most important things:

  1. 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 Func<T>.
  2. The view object is held for too long: Think of a scenario when 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<PrintView>>, which solves both of our problems. The code below shows the implementation:

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.

 

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.


2 Comments

Sharron Straney · October 6, 2018 at 5:29 am

Greetings! I’ve been reading your weblog for a long time now and finally got the bravery to go ahead and give you a shout out from Houston Tx! Just wanted to say keep up the great work!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.