async-expiring-lazy icon indicating copy to clipboard operation
async-expiring-lazy copied to clipboard

AsyncExpiringLazy

Async Expiring Lazy

See blog article for more background details and a follow up post for further update.

Installation

Grab the Nuget package called Strathweb.AsyncExpiringLazy. Built with .NET Standard 1.1 and compatible with:

  • .NET 4.5+
  • .NET Core
  • Xamarin
  • Mono
  • Universal Windows Platform

Latest version: Nuget

Usage

The code sample shows creating a lazy epxiring value with AsyncExpiringLazy<T> and letting it get recreated on first reuse after expiration.

var testInstance = new AsyncExpiringLazy<TokenResponse>(async metadata =>
{
    await Task.Delay(1000);
    return new ExpirationMetadata<TokenResponse>
    {
        Result = new TokenResponse
        {
            AccessToken = Guid.NewGuid().ToString()
        }, ValidUntil = DateTimeOffset.UtcNow.AddSeconds(2)
    };
});

// 1. check if value is created - shouldn't
Assert.False(await testInstance.IsValueCreated());

// 2. fetch lazy expiring value
var token = await testInstance.Value();

// 3a. verify it is created now
Assert.True(await testInstance.IsValueCreated());

// 3b. verify it is not null
Assert.NotNull(token.AccessToken);

// 4. fetch the value again. Since it's lifetime is 2 seconds, it should be still the same
var token2 = await testInstance.Value();
Assert.Same(token, token2);

// 5. sleep for 2 seconds to let the value expire
await Task.Delay(2000);

// 6a. verify the value expired
Assert.False(await testInstance.IsValueCreated());

// 6b. fetch again
var token3 = await testInstance.Value();

// 7. verify we now have a new (recreated) value - as the previous one expired
Assert.NotSame(token2, token3);

// 8. invalidate the value manually before it has a chance to expire
await testInstance.Invalidate();

// 9. check if value is created - shouldn't anymore
Assert.False(await testInstance.IsValueCreated());

The next code sample shows creating a lazy epxiring value with AsyncExpiringEager<T> and letting it get recreated silently as soon as it expires. Pay attetion to the different from the previous example at step 6a.

using var testInstance = new AsyncExpiringEager<TokenResponse>(async metadata =>
{
    await Task.Delay(1000);
    return new ExpirationMetadata<TokenResponse>
    {
        Result = new TokenResponse
        {
            AccessToken = Guid.NewGuid().ToString()
        }, ValidUntil = DateTimeOffset.UtcNow.AddSeconds(2)
    };
});

// 1. check if value is created - shouldn't
Assert.False(testInstance.IsValueCreated());

// 2. fetch lazy expiring value
var token = await testInstance.Value();

// 3a. verify it is created now
Assert.True(testInstance.IsValueCreated());

// 3b. verify it is not null
Assert.NotNull(token.AccessToken);

// 4. fetch the value again. Since it's lifetime is 2 seconds, it should be still the same
var token2 = await testInstance.Value();
Assert.Same(token, token2);

// 5. sleep for 2 seconds to let the value expire
await Task.Delay(2000);

// 6a. verify the value was eagerly created just before it expired
Assert.True(testInstance.IsValueCreated());

// 6b. fetch again
var token3 = await testInstance.Value();

// 7. verify we now have a new (recreated) value - as the previous one expired
Assert.NotSame(token2, token3);

// 8. invalidate the value manually before it has a chance to expire
testInstance.Invalidate();

// 9. check if value is created - shouldn't anymore
Assert.False(testInstance.IsValueCreated());

License

MIT