Skip to content

Instantly share code, notes, and snippets.

@seesharper
Last active February 3, 2016 13:33
Show Gist options
  • Select an option

  • Save seesharper/d0b9f1cf24940564f13a to your computer and use it in GitHub Desktop.

Select an option

Save seesharper/d0b9f1cf24940564f13a to your computer and use it in GitHub Desktop.
Injecting asynchronous services

Injecting asynchronous services

How often have you found yourself in a situation where you have some synchronous code where you would like to call an asynchronous method?

My guess is pretty often. Especially if you are working with legacy code and just starting to learn about async and await.

I am going to show you an example of such a situation and ways to deals with it.

Note: The sample code uses LightInject as the IoC container, but this example applies to all containers and even if you are doing "manual" dependency injection. Being the author of LightInject I felt it was a natural choice :)

Lets start off with a simple class that represents the configuration for any given application. By configuration I mean something that is external to the application itself and typically lives in app.config.

 public class Configuration
    {
        public Configuration(string meaningOfLife)
        {
            MeaningOfLife = meaningOfLife;
        }

        public string MeaningOfLife { get; }
    }

We also have this nice little interface called IConfigurationReader that is used to read the configuration from somewhere.

public interface IConfigurationReader
{
	Configuration Read();
}

The default implementation of this interface just reads the value from app.config.

public class ConfigurationReader : IConfigurationReader
{
    public Configuration Read()
    {
        return new Configuration(ConfigurationManager.AppSettings["MeaningOfLife"]);
    }
}

Finally we have this stupid little class that consumes the configuration.

public class Foo
{
    private readonly Configuration configuration;

    public Foo(Configuration configuration)
    {
        this.configuration = configuration;
    }

    public async Task<string> ExecuteAsync()
    {
        var meaningOfLife = configuration.MeaningOfLife;
        // assume calling another async method here.
        // we return the config value just to return something. 
        return await Task<string>.FromResult(meaningOfLife);
    } 
}

In LightInject, we typically register services in a class that implements the ICompositionRoot interface.

public class CompositionRoot : ICompositionRoot
{
    public void Compose(IServiceRegistry serviceRegistry)
    {
        serviceRegistry.Register<Foo>();
        serviceRegistry.Register<IConfigurationReader, ConfigurationReader>(new PerContainerLifetime());
        serviceRegistry.Register(factory => factory.GetInstance<IConfigurationReader>().Read());
    }       
}   

The we create and register the composition root with the container.

var container = new ServiceContainer();
container.RegisterFrom<CompositionRoot>();

Now from within an async method we can do something like.

var foo = container.GetInstance<Foo>();
var result = await container.ExecuteAsync();

Note: This is just an example of getting the Foo instance. Normally you should not call the container directly.

So now we have a class named Foo that gets its Configuration injected. Good stuff.

Enter asynchronous service resolution

What if we decided to move the configuration out of the app.config file and onto another server? This means that instead of just reading a file from the local disc, we will now be doing network calls which again pushes us towards reading the configuration asynchronously. Say that we start to use the the HttpClient class to make REST calls and the HttpClient does not even have a synchronous Get method. What to do?

First approach

public class HttpConfigurationReader : IConfigurationReader
{
    public Configuration Read()
    {
        HttpClient client = new HttpClient();
        var result = client.GetAsync("www.tempuru.org/api/configuration").Result
            .Content.ReadAsStringAsync().Result;
        return JsonConvert.DeserializeObject<Configuration>(result);
    }
}

That gets the job done, but we are blocking on an asynchronous operation by accessing the Result property. Actually we are doing it twice since we also need to read the content asynchronously.

Note: As a general rule of thumb we should NEVER block an asynchronous operation. Once we go async we need to be async all the way.

Second approach

In order to fix this we need to make the Read method asynchronous like this:

        public async Task<Configuration> Read()
        {
            HttpClient client = new HttpClient();
            var response = (await client.GetAsync("www.tempuru.org/api/configuration")).Content;
            var content = await response.ReadAsStringAsync();                
            return JsonConvert.DeserializeObject<Configuration>(content);
        }

That looks much better. We are no longer blocking anything since the Read method is now also awaitable. Let's name the method ReadAsync for consistency with other asynchronous methods.

Now it is time to see what kind of ramifications this has to our existing code. First of all we need to modify the Foo class.

public class Foo
{
    private readonly Task<Configuration> configuration;

	public Foo(Task<Configuration> configuration)
	{
	    this.configuration = configuration;
	}
	
	public async Task<string> ExecuteAsync()
	{
	    var meaningOfLife = (await configuration).MeaningOfLife;
	    // assume calling another async method here.
	    // we return the config value just to return something. 
	    return await Task<string>.FromResult(meaningOfLife);
	} 
}

That wasn't too bad. The only thing that has changed is that we are now taking a dependency upon Task<Configuration> instead of Configuration.

Next (dare I say task?) is to modify the composition root.

public class CompositionRoot : ICompositionRoot
{
    public void Compose(IServiceRegistry serviceRegistry)
    {
        serviceRegistry.Register<Foo>();
        serviceRegistry.Register<IConfigurationReader, HttpConfigurationReader>(new PerContainerLifetime());
        serviceRegistry.Register(factory => factory.GetInstance<IConfigurationReader>().ReadAsync());
    }       
}  

Nothing to write home about here either apart from that we are now using the HttpConfigurationReader and that the factory delegate that used to create a Configuration instance now returns a Task<Configuration> instance.

With a relative small change we have removed all code that blocked on asynchronous operations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment