Although you can register and inject a Task<ISceduler>
, Simple Injector does not support the registration of async factory methods, because its GetInstance
methods are synchronous.
As a matter of fact, no DI Container supports this, and neither should they. Object Composition should be fast and reliable and no I/O operations should take place during object composition.
Having I/O (and thus async) operations run during Object Composition causes the operation to become slow, unreliable and makes it much harder to test object construction (since the external I/O resource must be available during that time).
Instead, async operations should be moved either before Object Composition or after Object Composition. Before Object Composition means during application startup, which means one-time start-up initialization, while async operations that happen after Object Composition are triggered by the calls made to components on the constructed object graph.
What the correct solution is in your case depends on several factors.
In case your application only needs one IScheduler
in your application, it might be okay to call factory.GetScheduler
once at application startup and register the IScheduler
as Singleton
in the container. Here's a related discussion about doing async start-up initialization.
If however the IScheduler
can't be a singleton, this means that your code that uses IScheduler
needs to become asynchronous (what it probably already is). It might mean you need to inject an ISchedulerFactory
into components that need to use IScheduler
. This way you can await the GetScheduler
method.
Another common option is to create a Virtual Proxy implementation of the abstraction we are working with, in this case IScheduler
. This however does not work in the case of Quartz because
- The
IScheduler
interface has many members and creating a Virtual Proxy for that is very cumbersome.
- It requires all members of
IScheduler
to be async, but there quite a few that aren't async, such as IsStarted
. Creating an application-specific abstraction therefore makes much more sense.
This means to hide the use of IScheduler
behind an asynchronous application-specific abstraction. This abstraction will hide the complexity of working with a scheduler and by making its methods asynchronous you can allow a scheduler to be created lazily by implementation of this new abstraction. This implementation will be an adapter that hides the complexity of the scheduler.