File: SampleUsage.cs
Web Access
Project: src\test\Libraries\Microsoft.Extensions.Caching.Hybrid.Tests\Microsoft.Extensions.Caching.Hybrid.Tests.csproj (Microsoft.Extensions.Caching.Hybrid.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.Text.Json;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
 
namespace Microsoft.Extensions.Caching.Hybrid.Tests;
 
public class SampleUsage
{
    [Fact]
    public async Task DistributedCacheWorks()
    {
        var services = new ServiceCollection();
        services.AddDistributedMemoryCache();
        services.AddTransient<SomeDCService>();
        using ServiceProvider provider = services.BuildServiceProvider();
 
        var obj = provider.GetRequiredService<SomeDCService>();
        string name = "abc";
        int id = 42;
        var x = await obj.GetSomeInformationAsync(name, id);
        var y = await obj.GetSomeInformationAsync(name, id);
        Assert.NotSame(x, y);
        Assert.Equal(id, x.Id);
        Assert.Equal(name, x.Name);
        Assert.Equal(id, y.Id);
        Assert.Equal(name, y.Name);
    }
 
    [Fact]
    public async Task HybridCacheWorks()
    {
        var services = new ServiceCollection();
        services.AddHybridCache();
        services.AddTransient<SomeHCService>();
        using ServiceProvider provider = services.BuildServiceProvider();
 
        var obj = provider.GetRequiredService<SomeHCService>();
        string name = "abc";
        int id = 42;
        var x = await obj.GetSomeInformationAsync(name, id);
        var y = await obj.GetSomeInformationAsync(name, id);
        Assert.NotSame(x, y);
        Assert.Equal(id, x.Id);
        Assert.Equal(name, x.Name);
        Assert.Equal(id, y.Id);
        Assert.Equal(name, y.Name);
    }
 
    [Fact]
    public async Task HybridCacheNoCaptureWorks()
    {
        var services = new ServiceCollection();
        services.AddHybridCache();
        services.AddTransient<SomeHCServiceNoCapture>();
        using ServiceProvider provider = services.BuildServiceProvider();
 
        var obj = provider.GetRequiredService<SomeHCServiceNoCapture>();
        string name = "abc";
        int id = 42;
        var x = await obj.GetSomeInformationAsync(name, id);
        var y = await obj.GetSomeInformationAsync(name, id);
        Assert.NotSame(x, y);
        Assert.Equal(id, x.Id);
        Assert.Equal(name, x.Name);
        Assert.Equal(id, y.Id);
        Assert.Equal(name, y.Name);
    }
 
    [Fact]
    public async Task HybridCacheNoCaptureObjReuseWorks()
    {
        var services = new ServiceCollection();
        services.AddHybridCache();
        services.AddTransient<SomeHCServiceNoCaptureObjReuse>();
        using ServiceProvider provider = services.BuildServiceProvider();
 
        var obj = provider.GetRequiredService<SomeHCServiceNoCaptureObjReuse>();
        string name = "abc";
        int id = 42;
        var x = await obj.GetSomeInformationAsync(name, id);
        var y = await obj.GetSomeInformationAsync(name, id);
        Assert.Same(x, y);
        Assert.Equal(id, x.Id);
        Assert.Equal(name, x.Name);
    }
 
    public class SomeDCService(IDistributedCache cache)
    {
        public async Task<SomeInformation> GetSomeInformationAsync(string name, int id, CancellationToken token = default)
        {
            var key = $"someinfo:{name}:{id}"; // unique key for this combination
 
            var bytes = await cache.GetAsync(key, token); // try to get from cache
            SomeInformation info;
            if (bytes is null)
            {
                // cache miss; get the data from the real source
                info = await SomeExpensiveOperationAsync(name, id, token);
 
                // serialize and cache it
                bytes = SomeSerializer.Serialize(info);
                await cache.SetAsync(key, bytes, token);
            }
            else
            {
                // cache hit; deserialize it
                info = SomeSerializer.Deserialize<SomeInformation>(bytes);
            }
 
            return info;
        }
    }
 
    public class SomeHCService(HybridCache cache)
    {
        public async Task<SomeInformation> GetSomeInformationAsync(string name, int id, CancellationToken token = default)
        {
            return await cache.GetOrCreateAsync(
                $"someinfo:{name}:{id}", // unique key for this combination
                async ct => await SomeExpensiveOperationAsync(name, id, ct),
                cancellationToken: token);
        }
    }
 
    // this is the work we're trying to cache
    private static Task<SomeInformation> SomeExpensiveOperationAsync(string name, int id,
        CancellationToken token = default)
    {
        _ = token;
        return Task.FromResult(new SomeInformation { Id = id, Name = name });
    }
 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S3398:\"private\" methods called only by inner classes should be moved to those classes",
        Justification = "Allow future sharing")]
    private static Task<SomeInformationReuse> SomeExpensiveOperationReuseAsync(string name, int id,
        CancellationToken token = default)
    {
        _ = token;
        return Task.FromResult(new SomeInformationReuse { Id = id, Name = name });
    }
 
    public class SomeHCServiceNoCapture(HybridCache cache)
    {
        public async Task<SomeInformation> GetSomeInformationAsync(string name, int id, CancellationToken token = default)
        {
            return await cache.GetOrCreateAsync(
                $"someinfo:{name}:{id}", // unique key for this combination
                (name, id), // all of the state we need for the final call, if needed
                static async (state, token) =>
                    await SomeExpensiveOperationAsync(state.name, state.id, token),
                cancellationToken: token);
        }
    }
 
    public class SomeHCServiceNoCaptureObjReuse(HybridCache cache, CancellationToken token = default)
    {
        public async Task<SomeInformationReuse> GetSomeInformationAsync(string name, int id)
        {
            return await cache.GetOrCreateAsync(
                $"someinfo:{name}:{id}", // unique key for this combination
                (name, id), // all of the state we need for the final call, if needed
                static async (state, token) =>
                    await SomeExpensiveOperationReuseAsync(state.name, state.id, token),
                cancellationToken: token);
        }
    }
 
    private static class SomeSerializer
    {
        internal static T Deserialize<T>(byte[] bytes)
        {
            return JsonSerializer.Deserialize<T>(bytes)!;
        }
 
        internal static byte[] Serialize<T>(T info)
        {
            using var ms = new MemoryStream();
            JsonSerializer.Serialize(ms, info);
            return ms.ToArray();
        }
    }
 
    public class SomeInformation
    {
        public int Id { get; set; }
        public string? Name { get; set; }
    }
 
    [ImmutableObject(true)]
    public sealed class SomeInformationReuse
    {
        public int Id { get; set; }
        public string? Name { get; set; }
    }
}