// 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); }