// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
namespace Microsoft.Extensions.Caching.Hybrid;
///
/// Provides multi-tier caching services building on backends.
///
public abstract class HybridCache
{
///
/// Asynchronously gets the value associated with the key if it exists, or generates a new entry using the provided key and a value from the given factory if the key is not found.
///
/// The type of additional state required by .
/// The type of the data being considered.
/// The key of the entry to look for or create.
/// Provides the underlying data service if the data is not available in the cache.
/// The state required for .
/// Additional options for this cache entry.
/// The tags to associate with this cache item.
/// The used to propagate notifications that the operation should be canceled.
/// The data, either from cache or the underlying data service.
public abstract ValueTask GetOrCreateAsync(string key, TState state, Func> factory,
HybridCacheEntryOptions? options = null, IEnumerable? tags = null, CancellationToken cancellationToken = default);
///
/// Asynchronously gets the value associated with the key if it exists, or generates a new entry using the provided key and a value from the given factory if the key is not found.
///
/// The type of the data being considered.
/// The key of the entry to look for or create.
/// Provides the underlying data service if the data is not available in the cache.
/// Additional options for this cache entry.
/// The tags to associate with this cache item.
/// The used to propagate notifications that the operation should be canceled.
/// The data, either from cache or the underlying data service.
public ValueTask GetOrCreateAsync(string key, Func> factory,
HybridCacheEntryOptions? options = null, IEnumerable? tags = null, CancellationToken cancellationToken = default)
=> GetOrCreateAsync(key, factory, WrappedCallbackCache.Instance, options, tags, cancellationToken);
private static class WrappedCallbackCache // per-T memoized helper that allows GetOrCreateAsync and GetOrCreateAsync to share an implementation
{
// for the simple usage scenario (no TState), pack the original callback as the "state", and use a wrapper function that just unrolls and invokes from the state
public static readonly Func>, CancellationToken, ValueTask> Instance = static (callback, ct) => callback(ct);
}
///
/// Asynchronously sets or overwrites the value associated with the key.
///
/// The type of the data being considered.
/// The key of the entry to create.
/// The value to assign for this cache entry.
/// Additional options for this cache entry.
/// The tags to associate with this cache entry.
/// The used to propagate notifications that the operation should be canceled.
public abstract ValueTask SetAsync(string key, T value, HybridCacheEntryOptions? options = null, IEnumerable? tags = null, CancellationToken cancellationToken = default);
///
/// Asynchronously removes the value associated with the key if it exists.
///
public abstract ValueTask RemoveAsync(string key, CancellationToken cancellationToken = default);
///
/// Asynchronously removes the value associated with the key if it exists.
///
/// Implementors should treat null as empty
public virtual ValueTask RemoveAsync(IEnumerable keys, CancellationToken cancellationToken = default)
{
return keys switch
{
// for consistency with GetOrCreate/Set: interpret null as "none"
null or ICollection { Count: 0 } => default,
ICollection { Count: 1 } => RemoveAsync(keys.First(), cancellationToken),
_ => ForEachAsync(this, keys, cancellationToken),
};
// default implementation is to call RemoveAsync for each key in turn
static async ValueTask ForEachAsync(HybridCache @this, IEnumerable keys, CancellationToken cancellationToken)
{
foreach (var key in keys)
{
await @this.RemoveAsync(key, cancellationToken).ConfigureAwait(false);
}
}
}
///
/// Asynchronously removes all values associated with the specified tags.
///
/// Implementors should treat null as empty
public virtual ValueTask RemoveByTagAsync(IEnumerable tags, CancellationToken cancellationToken = default)
{
return tags switch
{
// for consistency with GetOrCreate/Set: interpret null as "none"
null or ICollection { Count: 0 } => default,
ICollection { Count: 1 } => RemoveByTagAsync(tags.Single(), cancellationToken),
_ => ForEachAsync(this, tags, cancellationToken),
};
// default implementation is to call RemoveByTagAsync for each key in turn
static async ValueTask ForEachAsync(HybridCache @this, IEnumerable keys, CancellationToken cancellationToken)
{
foreach (var key in keys)
{
await @this.RemoveByTagAsync(key, cancellationToken).ConfigureAwait(false);
}
}
}
///
/// Asynchronously removes all values associated with the specified tag.
///
public abstract ValueTask RemoveByTagAsync(string tag, CancellationToken cancellationToken = default);
}