44

I have a type Connections that requires asynchronous initialization. An instance of this type is consumed by several other types (e.g., Storage), each of which also require asynchronous initialization (static, not per-instance, and these initializations also depend on Connections). Finally, my logic types (e.g., Logic) consumes these storage instances. Currently using Simple Injector.

I've tried several different solutions, but there's always an antipattern present.


Explicit Initialization (Temporal Coupling)

The solution I'm currently using has the Temporal Coupling antipattern:

public sealed class Connections
{
  Task InitializeAsync();
}

public sealed class Storage : IStorage
{
  public Storage(Connections connections);
  public static Task InitializeAsync(Connections connections);
}

public sealed class Logic
{
  public Logic(IStorage storage);
}

public static class GlobalConfig
{
  public static async Task EnsureInitialized()
  {
    var connections = Container.GetInstance<Connections>();
    await connections.InitializeAsync();
    await Storage.InitializeAsync(connections);
  }
}

I've encapsulated the Temporal Coupling into a method, so it's not as bad as it could be. But still, it's an antipattern and not as maintainable as I'd like.


Abstract Factory (Sync-Over-Async)

A common proposed solution is an Abstract Factory pattern. However, in this case we're dealing with asynchronous initialization. So, I could use Abstract Factory by forcing the initialization to run synchronously, but this then adopts the sync-over-async antipattern. I really dislike the sync-over-async approach because I have several storages and in my current code they're all initialized concurrently; since this is a cloud application, changing this to be serially synchronous would increase startup time, and parallel synchronous is also not ideal due to resource consumption.


Asynchronous Abstract Factory (Improper Abstract Factory Usage)

I can also use Abstract Factory with asynchronous factory methods. However, there's one major problem with this approach. As Mark Seeman comments here, "Any DI Container worth its salt will be able to auto-wire an [factory] instance for you if you register it correctly." Unfortunately, this is completely untrue for asynchronous factories: AFAIK there is no DI container that supports this.

So, the Abstract Asynchronous Factory solution would require me to use explicit factories, at the very least Func<Task<T>>, and this ends up being everywhere ("We personally think that allowing to register Func delegates by default is a design smell... If you have many constructors in your system that depend on a Func, please take a good look at your dependency strategy."):

public sealed class Connections
{
  private Connections();
  public static Task<Connections> CreateAsync();
}

public sealed class Storage : IStorage
{
  // Use static Lazy internally for my own static initialization
  public static Task<Storage> CreateAsync(Func<Task<Connections>> connections);
}

public sealed class Logic
{
  public Logic(Func<Task<IStorage>> storage);
}

This causes several problems of its own:

  1. All my factory registrations have to pull dependencies out of the container explicitly and pass them to CreateAsync. So the DI container is no longer doing, you know, dependency injection.
  2. The results of these factory calls have lifetimes that are no longer managed by the DI container. Each factory is now responsible for lifetime management instead of the DI container. (With the synchronous Abstract Factory, this is not an issue if the factory is registered appropriately).
  3. Any method actually using these dependencies would need to be asynchronous - since even the logic methods must await for the storage/connections initialization to complete. This is not a big deal for me on this app since my storage methods are all asynchronous anyway, but it can be a problem in the general case.

Self Initialization (Temporal Coupling)

Another, less common, solution is to have each member of a type await its own initialization:

public sealed class Connections
{
  private Task InitializeAsync(); // Use Lazy internally

  // Used to be a property BobConnection
  public X GetBobConnectionAsync()
  {
    await InitializeAsync();
    return BobConnection;
  }
}

public sealed class Storage : IStorage
{
  public Storage(Connections connections);
  private static Task InitializeAsync(Connections connections); // Use Lazy internally
  public async Task<Y> IStorage.GetAsync()
  {
    await InitializeAsync(_connections);
    var connection = await _connections.GetBobConnectionAsync();
    return await connection.GetYAsync();
  }
}

public sealed class Logic
{
  public Logic(IStorage storage);
  public async Task<Y> GetAsync()
  {
    return await _storage.GetAsync();
  }
}

The problem here is that we're back to Temporal Coupling, this time spread out throughout the system. Also, this approach requires all public members to be asynchronous methods.


So, there's really two DI design perspectives that are at odds here:

  • Consumers want to be able to inject instances that are ready to use.
  • DI containers push hard for simple constructors.

The problem is - particularly with asynchronous initialization - that if DI containers take a hard line on the "simple constructors" approach, then they are just forcing the users to do their own initialization elsewhere, which brings its own antipatterns. E.g., why Simple Injector won't consider asynchronous functions: "No, such feature does not make sense for Simple Injector or any other DI container, because it violates a few important ground rules when it comes to dependency injection." However, playing strictly "by the ground rules" apparently forces other antipatterns that seem much worse.

The question: is there a solution for asynchronous initialization that avoids all antipatterns?


Update: Complete signature for AzureConnections (referred to above as Connections):

public sealed class AzureConnections
{
  public AzureConnections();

  public CloudStorageAccount CloudStorageAccount { get; }
  public CloudBlobClient CloudBlobClient { get; }
  public CloudTableClient CloudTableClient { get; }

  public async Task InitializeAsync();
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Can you elaborate on what the `Connections` class is? Is it under your control, or something provided by an external library? Can you show its complete signature? – Steven Aug 28 '17 at 17:21
  • @Steven: It's under my control. I've updated the question with its complete signature. This whole project is [open sourced here](https://github.com/StephenClearyApps/DotNetApis). – Stephen Cleary Aug 28 '17 at 17:23
  • 1
    Could you elaborate on the motivation for 'asynchronous initialisation'? Is it for performance reasons? If so, the [Virtual Proxy](https://en.wikipedia.org/wiki/Proxy_pattern#Virtual_Proxy) pattern sounds like it may fit. I discuss this a bit in section 8.3.6 of [my book](http://amzn.to/12p90MG). – Mark Seemann Aug 28 '17 at 17:29
  • A virtual proxy for `Connections` and the `Storage` types would require all public members to be asynchronous methods. So the current synchronous properties on `Connections` become asynchronous methods. Not terrible, but not ideal in the general case. – Stephen Cleary Aug 28 '17 at 17:34
  • I'm not sure that I follow, but then, I'm still not clear on what problem you're trying to solve... – Mark Seemann Aug 28 '17 at 17:38
  • This is applicable to any scenario with asynchronous initialization. In this particular case, I want to enable some CORS settings on the `CloudBlobClient` *before* it can be used to upload blobs. Similarly, the storage initialization is ensuring tables/containers are created before they're used. – Stephen Cleary Aug 28 '17 at 17:57
  • 2
    My thinking here is that the focus is too much on implementation concern. I would treat `AzureConnections` like a 3rd party dependency and encapsulate it behind an abstraction whose implementation would handle calling the async initialization (temporal) code. – Nkosi Aug 28 '17 at 18:01
  • @Nkosi: But how would you prevent the abstraction from having an asynchronous initialization itself? – Stephen Cleary Aug 28 '17 at 18:10
  • 1
    @StephenCleary It wouldn't. `AzureConnections` and the likes are code you do not control. No getting away from its constrained implementation but you can decided within your implementation when to call asynchronous initialization. Similar to the self initialization (temporal coupling) but called when you see fit and not leaked by your abstraction. – Nkosi Aug 28 '17 at 18:16
  • @Nkosi: So that's not any better than adding self-initialization to `AzureConnections`, then. It's still temporal coupling, spread through the system, and prevents the use of properties. – Stephen Cleary Aug 28 '17 at 18:49
  • @StephenCleary, are you really looking for asynchronous initialization or initialization that needs to call into APIs implemented using async-await? – Paulo Morgado Aug 28 '17 at 21:15
  • @PauloMorgado: Those two options have the same meaning to me. – Stephen Cleary Aug 28 '17 at 21:25
  • @StephenCleary, no they don't. That's why you're asking this question, isn't it? So, would you be up to something like `container.Register(() => { var service = new Service(...); service.InitializeAsync().GetAwaiter().GetResult(); });`? – Paulo Morgado Aug 28 '17 at 22:22
  • @PauloMorgado: No, that's using the sync-over-async antipattern. – Stephen Cleary Aug 28 '17 at 23:24
  • So, how would you envision a container doing that? – Paulo Morgado Aug 28 '17 at 23:35
  • @PauloMorgado: Personally, I'd like to see `Container.GetInstanceAsync` support. – Stephen Cleary Aug 29 '17 at 00:12
  • @StephenCleary, isn't that also an anti-pattern? I only use `container.GetInstance` on registration. Other than that, I just declare constructors with dependencies. If I really have to, I take a dependency on `IServiceProvider` which only has `GetService(Type)`. – Paulo Morgado Aug 29 '17 at 05:58
  • @PauloMorgado: In this case (Azure Functions environment), I need to create my own root composition objects. – Stephen Cleary Aug 29 '17 at 11:46
  • @StephenCleary, sorry. Assuming `Container.GetInstanceAsync` is already a thing, can you post what you consider its ideal usage on your use case? – Paulo Morgado Aug 29 '17 at 14:37
  • 1
    @StephenCleary, is there a solution to your problem if you don't use a DI container (i.e., if you do [Pure DI](http://blog.ploeh.dk/2014/06/10/pure-di/))? How would it look like? – Yacoub Massad Aug 29 '17 at 14:49
  • @PauloMorgado: I'm not sure if I understand what you're asking. My desired usage would be `var logic = await container.GetInstanceAsync()`, where the `Logic` instance is returned after all initialization has completed, i.e., when it is usable. – Stephen Cleary Aug 29 '17 at 16:00
  • 1
    @YacoubMassad: I have a [few examples here](http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html). Personally, I prefer the static async factory pattern, so the root composition object would be constructed like `new Logic(await Storage.CreateAsync(await Connections.CreateAsync()));`. Of course, you lose the "injection" part of DI when doing it yourself, which becomes a maintenance burden. – Stephen Cleary Aug 29 '17 at 16:02
  • @StephenCleary, so basically you want the following feature from a DI container: It should contain a `GetInstanceAsync` method, and it should support static async constructors. For example, if during auto-wiring, it finds a class (e.g. Class1) with a private constructor it should look for a static method like `Task CreateInstance(IDependency dep1)` (using some convention), invoke it passing the dependencies the "auto-wire" way, await the result, and then continue the auto-wiring. I am not sure but I don't think current containers supports this, but I think this can be added. – Yacoub Massad Aug 29 '17 at 18:34
  • @StephenCleary, having said this, I think that the only place this would make sense is if you call `GetInstanceAsync` inside an async method. Where do you want to call this method from? – Yacoub Massad Aug 29 '17 at 18:35
  • @StephenCleary, by the way, although with Pure DI you have to inject everything yourself, you get many benefits. See this article for more details: http://www.dotnetcurry.com/patterns-practices/1285/clean-composition-roots-dependency-injection – Yacoub Massad Aug 29 '17 at 18:36
  • @YacoubMassad: The problem with adding async initialization to DI containers is that it forces consumers to consider implementation details; specifically, if any type in the dependency tree of `T` requires async initialization, then `GetInstance` would have to throw. Unless you *always* use `GetInstanceAsync`, which has its own burdens. And yes, I'm calling it from an async entry point (Azure Functions). – Stephen Cleary Aug 29 '17 at 18:53
  • @StephenCleary, I don't know much about Azure Functions. Are they their own composition root? Or do you compose them somewhere else? If they are their own Composition Root, then they should know everything about the "objects" that formulate the application (the Function in this case) and therefore it should know that some class needs to be constructed asynchronously and thus choose to use `GetInstanceAsync`. – Yacoub Massad Aug 29 '17 at 19:24
  • https://stackoverflow.com/questions/33872588/caching-the-result-from-a-n-async-factory-method-iff-it-doesnt-throw might spark some ideas for you and https://stackoverflow.com/questions/33524706/autofac-registering-an-async-factory-method and https://github.com/autofac/Autofac/issues/751 – Ruben Bartelink Aug 29 '17 at 21:07
  • @RubenBartelink: Hmmm, implementing the async-init types as awaitables themselves just might work. I'll play with it and post an update later. – Stephen Cleary Aug 29 '17 at 21:23
  • @StephenCleary This scheme worked quite well for me - in general you want to deal with the failures (and retries) at the point of consumption. Then the composition root can do some kick off work and/or it can explicitly bootstrap stuff you want to lift out of asyncness. I also got it to play well with WPF using INPC. (Have not touched it in 1y+ so can hardly recall any of it though!) – Ruben Bartelink Aug 29 '17 at 23:36
  • 2
    *"I have a type [...] that requires asynchronous initialization."* This looks more and more to me like an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Why does it require asynchronous initialisation? Initialising an object shouldn't require 'work'. The role of object initialisation is to ensure that the object is in a valid state. That's an OOD tenet that goes back to Bertrand Meyer in the mid-80's, if not earlier. (Apologies for adding this comment so late, but people keep pointing me to this post.) – Mark Seemann Mar 03 '20 at 07:46
  • 1
    @MarkSeemann "Why does it require asynchronous initialisation?" - any service that depends on IO, for example (as all IO in .NET _ostensibly_ should be async). A more concrete example is a service that needs to read files from disk _one-time_ during initialization. You could argue that that service should perform lazy initialization whenever any of its methods are invoked, but that doesn't work if the service is used along a hot-path or ultra-reliable path that cannot use async code nor perform any IO or exposes data through properties (which also cannot be async). – Dai Jun 15 '21 at 07:56

3 Answers3

24

This is a long answer. There's a summary at the end. Scroll down to the summary if you're in a hurry.

The problem you have, and the application you're building, is a-typical. It’s a-typical for two reasons:

  1. you need (or rather want) asynchronous start-up initialization, and
  2. Your application framework (azure functions) supports asynchronous start-up initialization (or rather, there seems to be little framework surrounding it).

This makes your situation a bit different from a typical scenario, which might make it a bit harder to discuss common patterns.

However, even in your case the solution is rather simple and elegant:

Extract initialization out of the classes that hold it, and move it into the Composition Root. At that point you can create and initialize those classes before registering them in the container and feed those initialized classes into the container as part of registrations.

This works well in your particular case, because you want to do some (one-time) start-up initialization. Start-up initialization is typically done before you configure the container (or sometimes after if it requires a fully composed object graph). In most cases I’ve seen, initialization can be done before, as can be done effectively in your case.

As I said, your case is a bit peculiar, compared to the norm. The norm is:

  • Start-up initialization is synchronous. Frameworks (like ASP.NET Core¹) typically do not support asynchronous initialization in the start-up phase.
  • Initialization often needs to be done per-request and just-in-time rather than per-application and ahead-of-time. Often components that need initialization have a short lifetime, which means we typically initialize such instance on first use (in other words: just-in-time).

There is usually no real benefit of doing start-up initialization asynchronously. There is no practical performance benefit because, at start-up time, there will only be a single thread running anyway (although we might parallelize this, that obviously doesn’t require async). Also note that although some application types might deadlock on doing synch-over-async, in the Composition Root we know exactly which application type we are using and whether or not this will be a problem or not. A Composition Root is always application-specific. In other words, when we have initialization in the Composition Root of a non-deadlocking application (e.g. ASP.NET Core, Azure Functions, etc), there is typically no benefit of doing start-up initialization asynchronously, except perhaps for the sake of sticking to the advised patterns & practices.

Because you know whether or not sync-over-async is a problem or not in your Composition Root, you could even decide to do the initialization on first use and synchronously. Because the amount of initialization is finite (compared to per-request initialization) there is no practical performance impact on doing it on a background thread with synchronous blocking if you wish. All you have to do is define a Proxy class in your Composition Root that makes sure that initialization is done on first use. This is pretty much the idea that Mark Seemann proposed as answer.

I was not familiar at all with Azure Functions, so this is actually the first application type (except Console apps of course) that I know of that actually supports async initialization. In most framework types, there is no way for users to do this start-up initialization asynchronously at all. Code running inside an Application_Start event in an ASP.NET application or in the Startup class of an ASP.NET Core application, for instance, there is no async. Everything has to be synchronous.

On top of that, application frameworks don’t allow you to build their framework root components asynchronously. So even if DI Containers would support the concept of doing asynchronous resolves, this wouldn’t work because of the ‘lack’ of support of application frameworks. Take ASP.NET Core’s IControllerActivator for instance. Its Create(ControllerContext) method allows you to compose a Controller instance, but the return type of the Create method is object, not Task<object>. In other words, even if DI Containers would provide us with a ResolveAsync method, it would still cause blocking because ResolveAsync calls would be wrapped behind synchronous framework abstractions.

In the majority of cases, you’ll see that initialization is done per-instance or at runtime. A SqlConnection, for instance, is typically opened per request, so each request needs to open its own connection. When you want to open the connection ‘just in time’, this inevitably results in application interfaces that are asynchronous. But be careful here:

If you create an implementation that is synchronous, you should only make its abstraction synchronous in case you are sure that there will never be another implementation (or proxy, decorator, interceptor, etc.) that is asynchronous. If you invalidly make the abstraction synchronous (i.e. have methods and properties that do not expose Task<T>), you might very well have a Leaky Abstraction at hand. This might force you to make sweeping changes throughout the application when you get an asynchronous implementation later on.

In other words, with the introduction of async you have to take even more care of the design of your application abstractions. This holds for your specific case as well. Even though you might only require start-up initialization now, are you sure that for the abstractions you defined (and AzureConnections as well) will never need just-in-time synchronous initialization? In case the synchronous behavior of AzureConnections is an implementation detail, you will have to make it async right away.

Another example of this is your INugetRepository. Its members are synchronous, but that is clearly a Leaky Abstraction, because the reason it is synchronous is because its implementation is synchronous. Its implementation, however, is synchronous because it makes use of a legacy NuGet package that only has a synchronous API. It’s pretty clear that INugetRepository should be completely async, even though its implementation is synchronous, because implementations are expected to communicate over the network, which is where asynchronicity makes sense.

In an application that applies async, most application abstractions will have mostly async members. When this is the case, it would be a no-brainer to make this kind of just-in-time initialization logic async as well; everything is already async.

Summary

  • In case you need start-up initialization: do it before or after configuring the container. This makes composing object graphs itself fast, reliable, and verifiable.
  • Doing initialization before configuring the container prevents Temporal Coupling, but might mean you will have to move initialization out of the classes that require it (which is actually a good thing).
  • Async start-up initialization is impossible in most application types. In the other application types it is typically unnecessary.
  • In case you require per-request or just-in-time initialization, there is no way around having asynchronous interfaces.
  • Be careful with synchronous interfaces if you’re building an asynchronous application, you might be leaking implementation details.

Footnotes

  1. ASP.NET Core actually does allow async start-up initialization, but not from within the Startup class. There are several ways to achieve this: either you implement and register hosted services that contain (or delegate to) the initialization, or trigger the async initialization from within the async Main method of the program class.
Steven
  • 166,672
  • 24
  • 332
  • 435
  • I found your footnote particularly useful in abstracting async initialization with: services.AddSingleton().AddHostedService(sp => sp.GetRequiredService()); Our developers then leverage IHostedService.StartAsync as the standard way to handle all async service initialization. – Eric Patrick Sep 19 '22 at 13:56
5

While I'm fairly sure the following isn't what you're looking for, can you explain why it doesn't address your question?

public sealed class AzureConnections
{
    private readonly Task<CloudStorageAccount> storage;

    public AzureConnections()
    {
        this.storage = Task.Factory.StartNew(InitializeStorageAccount);
        // Repeat for other cloud 
    }

    private static CloudStorageAccount InitializeStorageAccount()
    {
        // Do any required initialization here...
        return new CloudStorageAccount( /* Constructor arguments... */ );
    }

    public CloudStorageAccount CloudStorageAccount
    {
        get { return this.storage.Result; }
    }
}

In order to keep the design clear, I only implemented one of the cloud properties, but the two others could be done in a similar fashion.

The AzureConnections constructor will not block, even if it takes significant time to initialise the various cloud objects.

It will, on the other hand, start the work, and since .NET tasks behave like promises, the first time you try to access the value (using Result) it's going to return the value produced by InitializeStorageAccount.

I get the strong impression that this isn't what you want, but since I don't understand what problem you're trying to solve, I thought I'd leave this answer so at least we'd have something to discuss.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • When I talk about "asynchronous", I mean truly asynchronous operations - i.e., doing I/O, not using background threads. In my case, `CloudStorageAccount` is synchronous and not a problem, but consider the blob container: I want to enforce a single call to `CreateIfNotExistsAsync` *before* it's available to the rest of the code to be used. And while I can use sync-over-async (i.e., `CreateIfNotExists` or `Result`), this would serialize all of my initialization - doable but not ideal in a cloud environment. – Stephen Cleary Aug 29 '17 at 11:52
  • @StephenCleary I honestly don't understand what you mean, or the problem you're trying to solve. When I look at the `AzureConnections` class posted in the OP, it has three public read-only properties. According to the framework design guidelines, one should [avoid throwing exceptions from property getters](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/property). What happens if a client attempts to get the value from one of those properties before `InitializeAsync` has completed? – Mark Seemann Aug 29 '17 at 17:32
  • It gets the client object just fine, and can use it, even though the service properties haven't been set up. But if you consider the blob container, the user *must* call `CreateIfNotExistsAsync` before calling `UploadAsync`, otherwise the upload will fail. – Stephen Cleary Aug 29 '17 at 17:41
0

It looks like you are trying to do what I am doing with my proxy singleton class.

                services.AddSingleton<IWebProxy>((sp) => 
                {
                    //Notice the GetService outside the Task.  It was locking when it was inside
                    var data = sp.GetService<IData>();

                    return Task.Run(async () =>
                    {
                        try
                        {
                            var credentials = await data.GetProxyCredentialsAsync();
                            if (credentials != null)
                            {
                                return new WebHookProxy(credentials);
                            }
                            else
                            {
                                return (IWebProxy)null;
                            }
                        }
                        catch(Exception ex)
                        {
                            throw;
                        }
                    }).Result;  //Back to sync
                });
T Brown
  • 1,385
  • 13
  • 9
  • 1
    This is a hack that will make it "work" but this code is no longer async. Invoking `.Result` makes this code blocking, where as async/await is non-blocking. – tkit Dec 13 '21 at 15:38
  • 1
    Of course it is a hack... There is no async methods on the services.AddSingleton/Transient/Scoped.... So this is the only way to make it work. – T Brown Dec 17 '21 at 20:37