How to perform async initalization of lazy injection

The easiest way is have the thing you inject be a Lazy<Task<T>>, The factory would look something along the lines of

private Lazy<Task<Foo>> LazyFooFactory()
{
    return new Lazy<Task<Foo>>(InitFoo);
}

private async Task<Foo> InitFoo()
{
    //Other code as needed
    Foo result = await SomeSlowButAsyncronousCodeToGetFoo();
    //Other code as needed
    return result;
}

Used as the following

private readonly Lazy<Task<Foo>> _lazyFoo

public SomeClass(Lazy<Task<Foo>> lazyFoo)
{
    _lazyFoo = lazyFoo;
}

public async Task SomeFunc()
{
    //This should return quickly once the task as been started the first call
    // then will use the existing Task for subsequent calls.
    Task<Foo> fooTask = _lazyFoo.Value; 

    //This awaits for SomeSlowButAsyncronousCodeToGetFoo() to finish the first calling
    // then will used the existing result to return immediately for subsequent calls.
    var foo = await fooTask;

    DoStuffWithFoo(foo);
}

The function SomeSlowButAsyncronousCodeToGetFoo() is not called until the first calling of _lazyFoo.Value and subsequent calls will use the existing Task.Result value and not re-call the factory.


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:

  • the previously expensive object itself becomes simple; it not responsible anymore for the loading of the data.
  • the object graph becomes fast and verifiable.
  • it becomes easy to change the loading strategy from lazy to background loading, without anything in the system to change.

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)));