Lazy async initialization for expiring objects

Today I wanted to share something I found myself using quite a lot recently, and that is not supported out of the box by the .NET framework.

So, as part of the framework, we have Lazy<T>, which provides out of the box support for deferring the creation of a large or resource-intensive objects.

However, what if the object requires async operation to be created, and what if its value expires after some time, and it needs to be recomputed? Let’s have a look at how to solve this.

Sample use case

Let’s imagine the following scenario – you are making service calls to an HTTP API using a typical OAuth 2.0 client credentials flow. This means, you need to obtain the token from the identity server in order to be able to use it to call the resource server. Typically you’d only fetch the token once it’s needed, and the operation of retrieving the token should be async, as it’s network bound. Additionally, according to the OAuth spec, client credentials flow doesn’t support refresh tokens, so once the original access token expires, you’ll need to re-request a new one.

All of that means that we are dealing with a lazy initialization (get the token only once we need it for the first time), with an async operation (network bound) and with an object that will expire (access token has a limited lifetime).

Let’s call our construct AsyncExpiringLazy<T> and support this scenario in a generic way.

Building an AsyncExpiringLazy<T>

In addition to the construct itself, we will also need a simple wrapper around the result object and its expiration timestamp, so let’s start there.

So far so good – not much to explain there. Next let’s add AsyncExpiringLazy. Since properties can’t be async in C#, in our AsyncExpiringLazy, we’ll need to use an instance method as a way to fetch our underlying value.

Let’s first create the outline of the class, and then fill in the remaining blanks.

So the constructor will take in a value provider – the provider itself will receive the “previous/old”, expired (or expiring) value and will be responsible of providing a new one – wrapped in the ExpirationMetadata defining it’s expiration time.

Filling in the remaining blanks is surprisingly easy – it’s shown below. The only “trick” in the code is to use SemaphoreSlim for locking – since C# does not allow traditional lock statements to contain awaits.

Regarding invalidation, we can just reset the ExpirationMetadata back to its default value.

The code is shown below.

And that’s it.


Usage is quite simple and is shown in the next unit test:

You can create an instance of AsyncExpiringLazy at any point – for example it could be a field in some class of yours. You will need to pass in a delegate responsible for value creation – it will give you a chance to inspect the old value too if needed.

From there on, it’s all about accessing the value whenever you need it. And if it expires, AsyncExpiringLazy will re-create it on next access.

All the code for this article is located here on Github as .NET Standard 1.3 library. Hope this helps in some way.