Cache expensive results with ConcurrentDictionary

C#
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

/// <summary>Thread-safe cache that computes each value at most once.</summary>
public sealed class MemoCache<TKey, TValue> where TKey : notnull
{
    private readonly ConcurrentDictionary<TKey, Lazy<TValue>> _items = new();

    public TValue GetOrAdd(TKey key, Func<TKey, TValue> factory) =>
        _items.GetOrAdd(
                key,
                k => new Lazy<TValue>(() => factory(k), LazyThreadSafetyMode.ExecutionAndPublication))
            .Value;
}

public static class Demo
{
    public static async Task Main()
    {
        var calls = 0;
        var cache = new MemoCache<int, string>();
        var tasks = Enumerable.Range(1, 8).Select(_ => Task.Run(() => cache.GetOrAdd(42, key =>
        {
            Interlocked.Increment(ref calls);
            Thread.Sleep(50); // pretend this is a slow database call
            return $"value-{key}";
        })));
        var results = await Task.WhenAll(tasks);
        Console.WriteLine($"{results[0]} computed {calls} time(s)"); // value-42 computed 1 time(s)
    }
}