Partage via


Bibliothèque HybridCache dans ASP.NET Core

Important

Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.

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 --prerelease

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 est introuvable dans le cache principal (non-correspondance dans le cache), elle vérifie le cache secondaire, le cas échéant. 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 vérifie qu’un seul appelant simultané pour une clé donnée exécute la méthode de fabrique, et que tous les autres appelants attendent le résultat de cette exécution. 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),
            token: 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),
            token: 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 non expirées

Quand les données sous-jacentes des entrées de cache changent avant leur expiration, vous pouvez supprimer explicitement ces entrées. Les entrées à supprimer peuvent être spécifiées par clé. Quand une entrée est supprimée, elle est retirée des caches principal et secondaire.

Supprimer par clé

Les méthodes suivantes prennent en charge la suppression des entrées de cache par clé :

  • RemoveKeyAsync
  • RemoveKeysAsync

Remarque : Celles-ci seront remplacées par RemoveByKeyAsync et RemoveByKeysAsync à l’avenir.

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,
            token: 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 de valeurs dépassant cette taille sont journalisées, et aucune valeur n’est 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 de valeurs dépassant cette taille sont journalisées, et aucune valeur n’est 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 WithSerializer et WithSerializerFactory, 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)
        };
    }).WithSerializer<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)
    };
}).WithSerializerFactory<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

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.

Dans la mesure où une grande partie de l’utilisation de HybridCache est adaptée à partir du code IDistributedCache existant, HybridCache conserve ce comportement par défaut pour éviter d’introduire des bogues de concurrence. 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 changé après sa création.

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.

É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 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 :