Partager via


Bibliothèque HybridCache dans ASP.NET Core

Important

HybridCache est toujours en préversion, mais sera entièrement publié après .NET 9.0 dans une prochaine version mineure des extensions .NET.

Cet article explique comment configurer et utiliser la bibliothèque HybridCache dans une application ASP.NET Core. Pour accéder à une présentation de la bibliothèque, consultez la section HybridCache de la vue d’ensemble de la mise en cache.

Obtenir la bibliothèque

Installez le package Microsoft.Extensions.Caching.Hybrid.

dotnet add package Microsoft.Extensions.Caching.Hybrid --version "9.0.0-preview.7.24406.2"

Enregistrer le service

Ajoutez le service HybridCache au conteneur d’injection de dépendances en appelant AddHybridCache :

// Add services to the container.
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddAuthorization();

builder.Services.AddHybridCache();

Le code précédent inscrit le service HybridCache avec les options par défaut. L’API d’inscription peut également configurer les options et la sérialisation.

Obtenir et stocker des entrées de cache

Le service HybridCache fournit une méthode GetOrCreateAsync avec deux surcharges, qui acceptent une clé et :

  • Une méthode de fabrique.
  • Un état et une méthode de fabrique.

La méthode utilise la clé pour tenter de récupérer l’objet dans le cache principal. Si l’élément n’est pas trouvé dans le cache principal (un cache manque), il vérifie le cache secondaire s’il est configuré. Si les données sont introuvables également à cet emplacement (autre non-correspondance dans le cache), elle appelle la méthode de fabrique pour obtenir l’objet à partir de la source de données. Elle stocke ensuite l’objet dans les caches principal et secondaire. La méthode de fabrique n’est jamais appelée si l’objet est trouvé dans le cache principal ou secondaire (correspondance dans le cache).

Le service HybridCache garantit qu’un seul appelant simultané pour une clé donnée appelle la méthode d’usine, et tous les autres appelants attendent le résultat de cet appel. Le CancellationToken passé à GetOrCreateAsync représente l’annulation combinée de tous les appelants simultanés.

Surcharge principale de GetOrCreateAsync

La surcharge sans état de GetOrCreateAsync est recommandée pour la plupart des scénarios. Le code permettant de l’appeler est relativement simple. Voici un exemple :

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
            cancellationToken: token
        );
    }

    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

Surcharge alternative de GetOrCreateAsync

La surcharge alternative peut réduire la charge de traitement liée aux variables capturées et aux rappels par instance, mais au prix d’un code plus complexe. Dans la plupart des scénarios, l’augmentation des performances ne l’emporte pas sur la complexité du code. Voici un exemple d’utilisation de la surcharge alternative :

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            (name, id, obj: this),
            static async (state, token) =>
            await state.obj.GetDataFromTheSourceAsync(state.name, state.id, token),
            cancellationToken: token
        );
    }

    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

Méthode SetAsync

Dans de nombreux scénarios, GetOrCreateAsync est la seule API nécessaire. Toutefois, HybridCache dispose également de SetAsync pour stocker un objet dans le cache sans essayer de le récupérer au préalable.

Supprimer les entrées de cache par clé

Lorsque les données sous-jacentes d’une entrée de cache changent avant son expiration, supprimez explicitement l’entrée en appelant RemoveAsync avec la clé à l’entrée. Une surcharge vous permet de spécifier une collection de valeurs de clé.

Lorsqu’une entrée est supprimée, elle est supprimée des caches principaux et secondaires.

Supprimer les entrées du cache par balise

Les balises peuvent être utilisées pour regrouper les entrées de cache et les invalider ensemble.

Définissez des balises lors de l’appel de GetOrCreateAsync, comme illustré dans l’exemple suivant :

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        var tags = new List<string> { "tag1", "tag2", "tag3" };
        var entryOptions = new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromMinutes(1),
            LocalCacheExpiration = TimeSpan.FromMinutes(1)
        };
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
            entryOptions,
            tags,
            cancellationToken: token
        );
    }
    
    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

Supprimez toutes les entrées d’une balise spécifiée en appelant RemoveByTagAsync avec la valeur de la balise. Une surcharge vous permet de spécifier une collection de valeurs d’étiquette.

Lorsqu’une entrée est supprimée, elle est supprimée des caches principaux et secondaires.

Options

La méthode AddHybridCache peut être utilisée pour la configuration globale par défaut. L’exemple suivant montre comment configurer certaines des options disponibles :

// Add services to the container.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();

builder.Services.AddHybridCache(options =>
    {
        options.MaximumPayloadBytes = 1024 * 1024;
        options.MaximumKeyLength = 1024;
        options.DefaultEntryOptions = new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromMinutes(5),
            LocalCacheExpiration = TimeSpan.FromMinutes(5)
        };
    });

La méthode GetOrCreateAsync peut également accepter un objet HybridCacheEntryOptions pour remplacer les valeurs par défaut globales d’une entrée de cache spécifique. Voici un exemple :

public class SomeService(HybridCache cache)
{
    private HybridCache _cache = cache;

    public async Task<string> GetSomeInfoAsync(string name, int id, CancellationToken token = default)
    {
        var tags = new List<string> { "tag1", "tag2", "tag3" };
        var entryOptions = new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromMinutes(1),
            LocalCacheExpiration = TimeSpan.FromMinutes(1)
        };
        return await _cache.GetOrCreateAsync(
            $"{name}-{id}", // Unique key to the cache entry
            async cancel => await GetDataFromTheSourceAsync(name, id, cancel),
            entryOptions,
            tags,
            cancellationToken: token
        );
    }
    
    public async Task<string> GetDataFromTheSourceAsync(string name, int id, CancellationToken token)
    {
        string someInfo = $"someinfo-{name}-{id}";
        return someInfo;
    }
}

Pour plus d’informations sur les options, consultez le code source :

Limites

Les propriétés suivantes de HybridCacheOptions vous permettent de configurer des limites qui s’appliquent à toutes les entrées de cache :

  • MaximumPayloadBytes : taille maximale d’une entrée de cache. La valeur par défaut est de 1 Mo. Les tentatives de stockage des valeurs sur cette taille sont journalisées et la valeur n’est pas stockée dans le cache.
  • MaximumKeyLength : longueur maximale d’une clé de cache. La valeur par défaut est de 1 024 caractères. Les tentatives de stockage des valeurs sur cette taille sont journalisées et la valeur n’est pas stockée dans le cache.

Sérialisation

L’utilisation d’un cache secondaire hors processus nécessite une sérialisation. La sérialisation est configurée dans le cadre de l’inscription du service HybridCache. Les sérialiseurs spécifiques à un type ainsi que les sérialiseurs à usage général peuvent être configurés via les méthodes AddSerializer et AddSerializerFactory, chaînées à partir de l’appel de AddHybridCache. Par défaut, la bibliothèque gère string et byte[] de manière interne, et utilise System.Text.Json pour tout le reste. HybridCache peut également utiliser d’autres sérialiseurs, par exemple protobuf ou XML.

L’exemple suivant configure le service pour qu’il utilise un sérialiseur protobuf spécifique au type :

// Add services to the container.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();

builder.Services.AddHybridCache(options =>
    {
        options.DefaultEntryOptions = new HybridCacheEntryOptions
        {
            Expiration = TimeSpan.FromSeconds(10),
            LocalCacheExpiration = TimeSpan.FromSeconds(5)
        };
    }).AddSerializer<SomeProtobufMessage, 
        GoogleProtobufSerializer<SomeProtobufMessage>>();

L’exemple suivant configure le service pour qu’il utilise un sérialiseur protobuf à usage général, capable de gérer de nombreux types protobuf :

// Add services to the container.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization();

builder.Services.AddHybridCache(options =>
{
    options.DefaultEntryOptions = new HybridCacheEntryOptions
    {
        Expiration = TimeSpan.FromSeconds(10),
        LocalCacheExpiration = TimeSpan.FromSeconds(5)
    };
}).AddSerializerFactory<GoogleProtobufSerializerFactory>();

Le cache secondaire nécessite un magasin de données, par exemple Redis ou SqlServer. Pour utiliser Azure Cache pour Redis, par exemple :

  • Installez le package Microsoft.Extensions.Caching.StackExchangeRedis.

  • Créez une instance d’Azure Cache pour Redis.

  • Obtenez une chaîne de connexion qui se connecte à l’instance de Redis. Recherchez la chaîne de connexion en sélectionnant Afficher les clés d’accès dans la page Vue d’ensemble du portail Azure.

  • Stockez la chaîne de connexion dans la configuration de l’application. Par exemple, utilisez un fichier de secrets utilisateur qui ressemble au code JSON suivant, et spécifiez la chaîne de connexion dans la section ConnectionStrings. Remplacez <the connection string> par la chaîne de connexion réelle :

    {
      "ConnectionStrings": {
        "RedisConnectionString": "<the connection string>"
      }
    }
    
  • Inscrivez l’implémentation de IDistributedCache fournie par le package Redis dans le conteneur d’injection de dépendances. Pour ce faire, appelez AddStackExchangeRedisCache, et passez la chaîne de connexion. Par exemple :

    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = 
            builder.Configuration.GetConnectionString("RedisConnectionString");
    });
    
  • L’implémentation Redis de IDistributedCache est désormais disponible dans le conteneur d’injection de dépendances de l’application. HybridCache s’en sert en tant que cache secondaire, et utilise pour cela le sérialiseur configuré.

Pour plus d’informations, consultez l’exemple d’application de sérialisation HybridCache.

Stockage du cache

Par défaut, HybridCache utilise MemoryCache pour le stockage dans le cache principal. Les entrées de cache sont stockées in-process. Ainsi, chaque serveur dispose d’un cache distinct, qui est perdu à chaque redémarrage du processus serveur. Pour le stockage hors processus secondaire, par exemple Redis ou SQL Server, HybridCache utilise l’implémentation configurée de IDistributedCache, le cas échéant. Toutefois, même sans l’implémentation de IDistributedCache, le service HybridCache fournit toujours une mise en cache in-process et une protection contre les requêtes simultanées en grand nombre.

Optimiser les performances

Pour optimiser les performances, configurez HybridCache afin de réutiliser les objets et d’éviter les allocations de byte[].

Réutiliser des objets

En réutilisant des instances, HybridCache peut réduire la charge de traitement des allocations de processeur et d’objets associées à la désérialisation par appel. Cela peut entraîner une amélioration des performances dans les scénarios où les objets mis en cache sont volumineux ou utilisés fréquemment.

Dans le code existant classique qui utilise IDistributedCache, chaque récupération d’un objet à partir du cache entraîne une désérialisation. Ce comportement signifie que chaque appelant simultané obtient une instance distincte de l’objet, qui ne peut pas interagir avec d’autres instances. Il en résulte une cohérence de thread, car il n’existe aucun risque de modifications simultanées sur la même instance d’objet.

Étant donné que beaucoup d’utilisation de HybridCache sera adaptée à partir du code IDistributedCache existant, HybridCache préserve ce comportement par défaut pour éviter d’introduire des bogues d’accès concurrentiel. Toutefois, les objets sont intrinsèquement thread-safe si :

  • Leurs types sont immuables.
  • Le code ne les modifie pas.

Dans ce genre de situation, indiquez à HybridCache que les instances peuvent être réutilisées de manière sécurisée en :

  • Marquant le type en tant que sealed. Le mot clé sealed en C# signifie que la classe ne peut pas être héritée.
  • Appliquant l’attribut [ImmutableObject(true)] au type. L’attribut [ImmutableObject(true)] indique que l’état de l’objet ne peut pas être modifié après sa création.

Éviter les allocations de byte[]

HybridCache fournit également des API facultatives pour les implémentations de IDistributedCache, afin d’éviter les allocations de byte[]. Cette fonctionnalité est implémentée par les préversions des packages Microsoft.Extensions.Caching.StackExchangeRedis et Microsoft.Extensions.Caching.SqlServer. Pour plus d’informations, consultez IBufferDistributedCache. Voici les commandes CLI .NET pour installer les packages :

dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis --prerelease
dotnet add package Microsoft.Extensions.Caching.SqlServer --prerelease

Implémentations personnalisées de HybridCache

Une implémentation concrète de la classe abstraite HybridCache est incluse dans l’infrastructure partagée, et est fournie via une injection de dépendances. Toutefois, les développeurs sont invités à fournir des implémentations personnalisées de l’API.

Compatibilité

La bibliothèque HybridCache prend en charge les runtimes .NET plus anciens, jusqu’à .NET Framework 4.7.2 et .NET Standard 2.0.

Ressources supplémentaires

Pour plus d’informations sur HybridCache, consultez les ressources suivantes :