Created
February 2, 2016 20:13
-
-
Save seesharper/e9002844f2671b5550f3 to your computer and use it in GitHub Desktop.
Injecting asynchronous services
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 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