0
votes

I would to call an asynchronous function in an Unity injection factory, like so...

Container.RegisterType<HttpClient>(new InjectionFactory(
                async c => await Create()));

... but it says...

Error CS4010: Cannot convert async lambda expression to delegate type 'Func'. An async lambda expression may return void, Task or Task, none of which are convertible to 'Func'.

Is there a way around this?

1
Async and DI dont' play well. I'd bind an async factory instead. So, you might write an IClientFactory with method GetClientAsync and bind that instead of HttpClient. - spender
What does the InjectionFactory constructor take? it looks like it takes Func<IUnityContainer, object> - but, that's not async compatible - you'd need something like Func<IUnityContainer, Task<object>> - without that, you can't do this - Marc Gravell♦
@spender So resolve the factory and then call it asynchronous? That works because I'm creating the context before registering it. - Ian Warburton
Oh maybe not, I can't push awaits all the way to the top of the stack. Guess I will have to abandon and call the context's config when creating some UI. - Ian Warburton

1 Answers

1
votes

Register the factory as Task<HttpClient> using the DelegateInjectionFactory. Then you can await it after it is injected, in code that you control.

public static IUnityContainer CompositionRoot()
{
    var container = new Unity.UnityContainer();
    container.RegisterType<Application>();
    container.RegisterType<Task<HttpClient>>
    (
        new DelegateInjectionFactory
        (
            new Func<Task<HttpClient>>
            (
                async () => await Create()
            )
        )
    );
    return container;
}

public static async Task<HttpClient> Create()
{
    await Task.Delay(1);  //Simulate doing something asynchronous
    return new HttpClient();
}

Example of a class that would receive the injection:

public class Example
{
    protected readonly Task<HttpClient> _client; //Injected

    public Example(Task<HttpClient> client)
    {
        _client = client;
    }
    public async Task<string> Run()
    {
        var result = await (await _client).GetAsync("http://www.StackOverflow.com");
        var text = await result.Content.ReadAsStringAsync();
        return text;
    }
}

Now Create() will be executed asynchronously, but is not awaited by the container (which isn't set up for that). Instead, it is awaited by your own code, in the class that consumes it, which you have control over.

Note: While this example demonstrates that it is possible, I would probably not do this in production code. It is more common to write a factory class, give it an async method, inject the factory class, and await the method in the class receiving the injection.