Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Castle Windsor lazy load services

Occasionally I find myself in a situation where I need to resolve a service only if a certain condition is met. For example, a user might select to send an email or an sms notification. I would like to lazy load the email or sms service depending on what the user chooses so that I don't have to load both of them and waste resources (what if there were, for example, 10 options for the user...?).

The problem I have is with using the container outside of my bootstrap code (I dont want my code dependent on th container). I can't seem to find a way around using the container for lazy loading services (unless I create the needed services by hand and do all the DI manually). Is this a situation where the rules can be bent or is there a better way of doing it?

like image 616
orourkedd Avatar asked Dec 26 '12 15:12

orourkedd


3 Answers

If you are using Castle Windsor 3.0 or newer, you can use lazy resolving.

See What's new in Windsor 3 for more details.

Registration process changes a bit (new component loader must be registered).

After that, you are just registering components as always, but resolve dependencies as Lazy<T> instead of T. Until you won't access .Value property of your Lazy<T>, dependency will not be resolved, so you can pass few lazily evaluated objects and access only the one you will need and when you need.

If you have more options to the user, maybe you should consider creating some sort of abstract factory interface. You would then register and resolve only this factory and the factory itself would create appropriate service for sending notifications (be it a mail or a sms or any other option). Implementation of the factory can be coded by hand or Castle Windsor can you with that (I think from version 3.0).

Often when I do use such factory, I implement it by hand and pass container as it's dependency, so only factory implementation depends on my container.

like image 191
Marcin Deptuła Avatar answered Nov 04 '22 13:11

Marcin Deptuła


Just an example to simplify (based on Marcin Deptuła answer)

// activate Lazy initialization feature  for all Components
.Register(Component.For<ILazyComponentLoader>().ImplementedBy<LazyOfTComponentLoader>())     
// register rest of component(s)
.Register(Component.For<IIssueRepository>().ImplementedBy<IssueRepository>()) 
. ....

resolve lazily (property injection)

public Lazy<IIssueRepository> IssueRepository { get; set; }
IssueRepository.Value.GetLastIssue();

resolve normal (property injection)

public IIssueRepository IssueRepository { get; set; }
IssueRepository.GetLastIssue();
like image 25
Soren Avatar answered Nov 04 '22 12:11

Soren


In general, you can do that with the Typed Factory Facility.

In a nutshell, when you resolve the component that uses those services, instead of giving it an email or sms service, you give it a factory that can create them (which is defined by you, without references to the container)

The facility will take care of "implementing" your factory (from the interface you create), so there's very little to do.

like image 26
Diego Mijelshon Avatar answered Nov 04 '22 14:11

Diego Mijelshon