Let's say we want to inject an object that is expensive to create (let's say it does initialization from a database), so we would typically use some kind of factory or Lazy<T>
. However, if we're injecting this object into an MVC or WebApi controller that is using async action methods, we don't want to block these methods on the expensive I/O operation when the Lazy object is initialized, which defeats the purpose of using async.
Certainly, I could create an "initlize" method that is async, but that violates a number of principles.
What's the best option for accessing and initializing the injected object in a lazy and async way?
All the components that are part of your object graph and are auto-wired by the container should be very lightweight to create, because injection constructors should be simple. Anything that is either runtime data, or one-time data that is costly to create, should not be injected directly into the constructor of a component that is part of your object graph. Async even exaggerates this, because constructors can never be asynchronous; you can't use await in the body of a constructor.
So if a component depends on some expensive to create data, this data should be loaded lazily, outside of the constructor. This way the building of the object graph becomes fast and the creation of your controllers doesn't get blocked.
So as @ScottChamberlain already said, probably the nicest way is to mix Lazy<T>
with Task<T>
to become Lazy<Task<T>>
. You would probably gain the best results if you inject this Lazy<Task<T>>
into the constructor of your 'expensive component' (making that component itself lightweight again). This has a few clear benefits:
As an example of the last point, allowing the data to be loaded in the background can be simply done as follows:
Task<ExpensiveData> data = Task.Run(LoadExpensiveData);
container.RegisterSingleton<ISomeClass>(
new SomeClass(new Lazy<Task<ExpensiveData>>(() => data)));