Not
Åtkomst till denna sida kräver auktorisation. Du kan prova att logga in eller byta katalog.
Åtkomst till denna sida kräver auktorisation. Du kan prova att byta katalog.
I den här artikeln får du lära dig om olika cachelagringsmekanismer. Cachelagring är att lagra data i ett mellanliggande lager, vilket gör efterföljande datahämtningar snabbare. Konceptuellt är cachelagring en strategi för prestandaoptimering och designöverväganden. Cachelagring kan avsevärt förbättra appprestanda genom att göra sällan föränderliga (eller dyra att hämta) data mer tillgängliga. Den här artikeln innehåller tre metoder för cachelagring och exempel på källkod för var och en:
- Microsoft.Extensions.Caching.Memory: Minnesintern cachelagring för scenarier med en server
- Microsoft.Extensions.Caching.Hybrid: Hybridcachelagring som kombinerar minnesintern och distribuerad cachelagring med ytterligare funktioner
- Microsoft.Extensions.Caching.Distributed: Distribuerad cachelagring för scenarier med flera servrar
Viktigt!
Det finns två MemoryCache klasser i .NET, en i System.Runtime.Caching namnområdet och den andra i Microsoft.Extensions.Caching namnområdet:
Även om den här artikeln fokuserar på cachelagring innehåller den System.Runtime.Caching inte NuGet-paketet. Alla referenser till MemoryCache finns i Microsoft.Extensions.Caching namnområdet.
Alla paket är Microsoft.Extensions.* redo för beroendeinmatning (DI). Gränssnitten IMemoryCache, HybridCacheoch IDistributedCache kan användas som tjänster.
Cachelagring i minnet
I det här avsnittet får du lära dig mer om paketet Microsoft.Extensions.Caching.Memory . Den aktuella implementeringen av IMemoryCache är en omslutning runt ConcurrentDictionary<TKey,TValue>, som exponerar ett funktionsrikt API. Inmatningar i cachen representeras av ICacheEntry och kan vara vilket som helst object. Den minnesinterna cachelösningen är bra för appar som körs på en enda server, där cachelagrade data använder minne i appens process.
Tips/Råd
För scenarier med cachelagring med flera servrar bör du överväga metoden distribuerad cachelagring som ett alternativ till minnesintern cachelagring.
Api för minnesintern cachelagring
Cachekonsumenten har kontroll över både glidande och absoluta förfallodatum:
- ICacheEntry.AbsoluteExpiration
- ICacheEntry.AbsoluteExpirationRelativeToNow
- ICacheEntry.SlidingExpiration
Om du anger ett förfallodatum tas poster i cacheminnet bort om de inte nås inom förfallotiden. Konsumenter har ytterligare alternativ för att styra cacheposter via MemoryCacheEntryOptions. Varje ICacheEntry är kopplad till MemoryCacheEntryOptions, som exponerar förfallofunktionalitet med IChangeToken, prioritetsinställningar med CacheItemPriority och styr ICacheEntry.Size. De relevanta tilläggsmetoderna är:
- MemoryCacheEntryExtensions.AddExpirationToken
- MemoryCacheEntryExtensions.RegisterPostEvictionCallback
- MemoryCacheEntryExtensions.SetSize
- MemoryCacheEntryExtensions.SetPriority
Exempel på minnesintern cache
Om du vill använda standardimplementeringen IMemoryCache anropar AddMemoryCache du tilläggsmetoden för att registrera alla nödvändiga tjänster med DI. I följande kodexempel används den generiska värden för att exponera DI-funktioner:
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();
using IHost host = builder.Build();
Beroende på din .NET-arbetsbelastning kan du komma åt det IMemoryCache annorlunda, till exempel konstruktorinmatning. I det här exemplet använder du IServiceProvider-instansen för host och anropar en generisk GetRequiredService<T>(IServiceProvider)-extensionsmetod.
IMemoryCache cache =
host.Services.GetRequiredService<IMemoryCache>();
Med minnesinterna cachelagringstjänster registrerade och lösta via DI är du redo att börja cachelagra. Det här exemplet itererar genom bokstäverna i det engelska alfabetet "A" genom "Z". Typen record AlphabetLetter innehåller referensen till bokstaven och genererar ett meddelande.
file record AlphabetLetter(char Letter)
{
internal string Message =>
$"The '{Letter}' character is the {Letter - 64} letter in the English alphabet.";
}
Tips/Råd
Åtkomstmodifieraren file används på AlphabetLetter typen, eftersom den definieras i och endast används från Program.cs-filen . Mer information finns i filen (C#-referens). Om du vill se den fullständiga källkoden kan du läsa avsnittet Program.cs .
Exemplet innehåller en hjälpfunktion som itererar genom alfabetets bokstäver:
static async ValueTask IterateAlphabetAsync(
Func<char, Task> asyncFunc)
{
for (char letter = 'A'; letter <= 'Z'; ++letter)
{
await asyncFunc(letter);
}
Console.WriteLine();
}
I föregående C#-kod:
-
Func<char, Task> asyncFuncförväntas vid varje iteration, och skickar det aktuellaletter. - När alla bokstäver har bearbetats skrivs en tom rad till konsolen.
Om du vill lägga till objekt i cachen anropar du något av API:erna Createeller Set :
var addLettersToCacheTask = IterateAlphabetAsync(letter =>
{
MemoryCacheEntryOptions options = new()
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMilliseconds(MillisecondsAbsoluteExpiration)
};
_ = options.RegisterPostEvictionCallback(OnPostEviction);
AlphabetLetter alphabetLetter =
cache.Set(
letter, new AlphabetLetter(letter), options);
Console.WriteLine($"{alphabetLetter.Letter} was cached.");
return Task.Delay(
TimeSpan.FromMilliseconds(MillisecondsDelayAfterAdd));
});
await addLettersToCacheTask;
I föregående C#-kod:
- Variabeln
addLettersToCacheTaskdelegerar tillIterateAlphabetAsyncoch väntar. -
Func<char, Task> asyncFuncargumenteras om med en lambda. - Den
MemoryCacheEntryOptionsinstansieras med en absolut utgångstid i förhållande till nu. - Ett återanrop efter borttagningen registreras.
- Ett
AlphabetLetterobjekt instansieras och skickas till Set tillsammans medletterochoptions. - Bokstaven skrivs till konsolen som cachelagrad.
- Slutligen så returneras Task.Delay.
För varje bokstav i alfabetet skrivs en cachepost med förfallodatum och återanrop efter borttagning.
Återanropet efter borttagningen skriver information om det värde som togs bort till konsolen:
static void OnPostEviction(
object key, object? letter, EvictionReason reason, object? state)
{
if (letter is AlphabetLetter alphabetLetter)
{
Console.WriteLine($"{alphabetLetter.Letter} was evicted for {reason}.");
}
};
Nu när cachen har fyllts i väntar ett annat anrop till IterateAlphabetAsync , men den här gången anropar IMemoryCache.TryGetValuedu :
var readLettersFromCacheTask = IterateAlphabetAsync(letter =>
{
if (cache.TryGetValue(letter, out object? value) &&
value is AlphabetLetter alphabetLetter)
{
Console.WriteLine($"{letter} is still in cache. {alphabetLetter.Message}");
}
return Task.CompletedTask;
});
await readLettersFromCacheTask;
Om cache innehåller letter nyckeln och value är en instans av AlphabetLetter, så skrivs det till konsolen.
letter När nyckeln inte fanns i cacheminnet hade den avlägsnats och dess återanrop efter borttagning hade anropats.
Ytterligare tilläggsmetoder
IMemoryCache kommer med många praktiska tilläggsmetoder, inklusive en asynkron GetOrCreateAsync.
- CacheExtensions.Get
- CacheExtensions.GetOrCreate
- CacheExtensions.GetOrCreateAsync
- CacheExtensions.Set
- CacheExtensions.TryGetValue
Färdigställa allt
Hela exempelappens källkod är ett toppnivåprogram och kräver två NuGet-paket:
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();
using IHost host = builder.Build();
IMemoryCache cache =
host.Services.GetRequiredService<IMemoryCache>();
const int MillisecondsDelayAfterAdd = 50;
const int MillisecondsAbsoluteExpiration = 750;
static void OnPostEviction(
object key, object? letter, EvictionReason reason, object? state)
{
if (letter is AlphabetLetter alphabetLetter)
{
Console.WriteLine($"{alphabetLetter.Letter} was evicted for {reason}.");
}
};
static async ValueTask IterateAlphabetAsync(
Func<char, Task> asyncFunc)
{
for (char letter = 'A'; letter <= 'Z'; ++letter)
{
await asyncFunc(letter);
}
Console.WriteLine();
}
var addLettersToCacheTask = IterateAlphabetAsync(letter =>
{
MemoryCacheEntryOptions options = new()
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMilliseconds(MillisecondsAbsoluteExpiration)
};
_ = options.RegisterPostEvictionCallback(OnPostEviction);
AlphabetLetter alphabetLetter =
cache.Set(
letter, new AlphabetLetter(letter), options);
Console.WriteLine($"{alphabetLetter.Letter} was cached.");
return Task.Delay(
TimeSpan.FromMilliseconds(MillisecondsDelayAfterAdd));
});
await addLettersToCacheTask;
var readLettersFromCacheTask = IterateAlphabetAsync(letter =>
{
if (cache.TryGetValue(letter, out object? value) &&
value is AlphabetLetter alphabetLetter)
{
Console.WriteLine($"{letter} is still in cache. {alphabetLetter.Message}");
}
return Task.CompletedTask;
});
await readLettersFromCacheTask;
await host.RunAsync();
file record AlphabetLetter(char Letter)
{
internal string Message =>
$"The '{Letter}' character is the {Letter - 64} letter in the English alphabet.";
}
Du kan justera värdena för MillisecondsDelayAfterAdd och MillisecondsAbsoluteExpiration för att observera ändringar i beteendet vid utgången och borttagandet av cachelagrade poster. Följande är exempelutdata från körning av den här koden. (På grund av den icke-terministiska karaktären hos .NET-händelser kan dina utdata vara annorlunda.)
A was cached.
B was cached.
C was cached.
D was cached.
E was cached.
F was cached.
G was cached.
H was cached.
I was cached.
J was cached.
K was cached.
L was cached.
M was cached.
N was cached.
O was cached.
P was cached.
Q was cached.
R was cached.
S was cached.
T was cached.
U was cached.
V was cached.
W was cached.
X was cached.
Y was cached.
Z was cached.
A was evicted for Expired.
C was evicted for Expired.
B was evicted for Expired.
E was evicted for Expired.
D was evicted for Expired.
F was evicted for Expired.
H was evicted for Expired.
K was evicted for Expired.
L was evicted for Expired.
J was evicted for Expired.
G was evicted for Expired.
M was evicted for Expired.
N was evicted for Expired.
I was evicted for Expired.
P was evicted for Expired.
R was evicted for Expired.
O was evicted for Expired.
Q was evicted for Expired.
S is still in cache. The 'S' character is the 19 letter in the English alphabet.
T is still in cache. The 'T' character is the 20 letter in the English alphabet.
U is still in cache. The 'U' character is the 21 letter in the English alphabet.
V is still in cache. The 'V' character is the 22 letter in the English alphabet.
W is still in cache. The 'W' character is the 23 letter in the English alphabet.
X is still in cache. The 'X' character is the 24 letter in the English alphabet.
Y is still in cache. The 'Y' character is the 25 letter in the English alphabet.
Z is still in cache. The 'Z' character is the 26 letter in the English alphabet.
Eftersom den absoluta förfallotiden (MemoryCacheEntryOptions.AbsoluteExpirationRelativeToNow) har angetts tas alla cachelagrade objekt så småningom bort.
Cachelagring av Worker Service
En vanlig strategi för cachelagring av data är att uppdatera cacheminnet oberoende av de förbrukande datatjänsterna.
Worker Service-mallen är ett bra exempel eftersom den BackgroundService körs oberoende (eller i bakgrunden) från den andra programkoden. När ett program börjar köras som är värd för en implementering av IHostedService, börjar den motsvarande implementeringen, det vill säga BackgroundService eller "arbetaren", köras i samma process. Dessa värdbaserade tjänster registreras som singletons med DI genom tilläggsmetoden AddHostedService<THostedService>(IServiceCollection). Andra tjänster kan registreras med DI med valfri tjänstlivslängd.
Viktigt!
Tjänstens livslängd är viktig att förstå. När du anropar AddMemoryCache för att registrera alla cachelagringstjänster i minnet registreras tjänsterna som singletons.
Fototjänstscenario
Anta att du utvecklar en fototjänst som förlitar sig på API från tredje part som är tillgänglig via HTTP. Dessa fotodata ändras inte ofta, men det finns mycket av dem. Varje foto representeras av en enkel record:
namespace CachingExamples.Memory;
public readonly record struct Photo(
int AlbumId,
int Id,
string Title,
string Url,
string ThumbnailUrl);
I följande exempel ser du flera tjänster som registreras med DI. Varje tjänst har ett enda ansvar.
using CachingExamples.Memory;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient<CacheWorker>();
builder.Services.AddHostedService<CacheWorker>();
builder.Services.AddScoped<PhotoService>();
builder.Services.AddSingleton(typeof(CacheSignal<>));
using IHost host = builder.Build();
await host.StartAsync();
I föregående C#-kod:
- Den generiska värden skapas med standardvärden.
- Cachelagringstjänster i minnet registreras med AddMemoryCache.
- En
HttpClientinstans registreras förCacheWorkerklassen med AddHttpClient<TClient>(IServiceCollection). - Klassen
CacheWorkerär registrerad med AddHostedService<THostedService>(IServiceCollection). - Klassen
PhotoServiceär registrerad med AddScoped<TService>(IServiceCollection). - Klassen
CacheSignal<T>är registrerad med AddSingleton. -
hostinstansieras av byggaren och startas asynkront.
Det är PhotoService som ansvarar för att få foton som matchar angivna kriterier (eller filter):
using Microsoft.Extensions.Caching.Memory;
namespace CachingExamples.Memory;
public sealed class PhotoService(
IMemoryCache cache,
CacheSignal<Photo> cacheSignal,
ILogger<PhotoService> logger)
{
public async IAsyncEnumerable<Photo> GetPhotosAsync(Func<Photo, bool>? filter = default)
{
try
{
await cacheSignal.WaitAsync();
Photo[] photos =
(await cache.GetOrCreateAsync(
"Photos", _ =>
{
logger.LogWarning("This should never happen!");
return Task.FromResult(Array.Empty<Photo>());
}))!;
// If no filter is provided, use a pass-thru.
filter ??= _ => true;
foreach (Photo photo in photos)
{
if (!default(Photo).Equals(photo) && filter(photo))
{
yield return photo;
}
}
}
finally
{
cacheSignal.Release();
}
}
}
I föregående C#-kod:
- Konstruktorn kräver en
IMemoryCache,CacheSignal<Photo>, ochILogger. - Metoden
GetPhotosAsync:- Definierar en
Func<Photo, bool> filterparameter och returnerar enIAsyncEnumerable<Photo>. - Anropar och väntar på att
_cacheSignal.WaitAsync()ska släppas; detta säkerställer att cacheminnet fylls innan du kommer åt det. - Anropar
_cache.GetOrCreateAsync()och hämtar asynkront alla foton i cacheminnet. - Argumentet
factoryloggar en varning och returnerar en tom fotomatris – detta bör aldrig inträffa. - Varje foto i cachen itereras, filtreras och materialiseras med
yield return. - Slutligen återställs cachesignalen.
- Definierar en
Konsumenter av den här tjänsten kan anropa GetPhotosAsync metoden och hantera foton i enlighet med detta. Nej HttpClient krävs eftersom cachen innehåller fotona.
Den asynkrona signalen baseras på en inkapslad SemaphoreSlim instans inom en begränsad singleton av generisk typ. Förlitar CacheSignal<T> sig på en instans av SemaphoreSlim:
namespace CachingExamples.Memory;
public sealed class CacheSignal<T>
{
private readonly SemaphoreSlim _semaphore = new(1, 1);
/// <summary>
/// Exposes a <see cref="Task"/> that represents the asynchronous wait operation.
/// When signaled (consumer calls <see cref="Release"/>), the
/// <see cref="Task.Status"/> is set as <see cref="TaskStatus.RanToCompletion"/>.
/// </summary>
public Task WaitAsync() => _semaphore.WaitAsync();
/// <summary>
/// Exposes the ability to signal the release of the <see cref="WaitAsync"/>'s operation.
/// Callers who were waiting, will be able to continue.
/// </summary>
public void Release() => _semaphore.Release();
}
I föregående C#-kod används dekoratörsmönstret för att omsluta en instans av SemaphoreSlim. Eftersom CacheSignal<T> är registrerad som en singleton kan den användas över livslängden för alla tjänster med vilken generisk typ som helst: i det här fallet Photo. Den är ansvarig för att signalera seeding av cachen.
CacheWorker är en underklass av BackgroundService:
using System.Net.Http.Json;
using Microsoft.Extensions.Caching.Memory;
namespace CachingExamples.Memory;
public sealed class CacheWorker(
ILogger<CacheWorker> logger,
HttpClient httpClient,
CacheSignal<Photo> cacheSignal,
IMemoryCache cache) : BackgroundService
{
private readonly TimeSpan _updateInterval = TimeSpan.FromHours(3);
private bool _isCacheInitialized = false;
private const string Url = "https://jsonplaceholder.typicode.com/photos";
public override async Task StartAsync(CancellationToken cancellationToken)
{
await cacheSignal.WaitAsync();
await base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Updating cache.");
try
{
Photo[]? photos =
await httpClient.GetFromJsonAsync<Photo[]>(
Url, stoppingToken);
if (photos is { Length: > 0 })
{
cache.Set("Photos", photos);
logger.LogInformation(
"Cache updated with {Count:#,#} photos.", photos.Length);
}
else
{
logger.LogWarning(
"Unable to fetch photos to update cache.");
}
}
finally
{
if (!_isCacheInitialized)
{
cacheSignal.Release();
_isCacheInitialized = true;
}
}
try
{
logger.LogInformation(
"Will attempt to update the cache in {Hours} hours from now.",
_updateInterval.Hours);
await Task.Delay(_updateInterval, stoppingToken);
}
catch (OperationCanceledException)
{
logger.LogWarning("Cancellation acknowledged: shutting down.");
break;
}
}
}
}
I föregående C#-kod:
- Konstruktorn kräver en
ILogger,HttpClient, ochIMemoryCache. -
_updateIntervalär definierad för tre timmar. - Metoden
ExecuteAsync:- Loopar medan appen körs.
- Skickar en HTTP-begäran till
"https://jsonplaceholder.typicode.com/photos"och mappar svaret som en matris medPhotoobjekt. - Arrayen med foton placeras i
IMemoryCacheunder nyckeln"Photos". -
_cacheSignal.Release()anropas, vilket frigör alla konsumenter som väntade på signalen. - Anropet till Task.Delay inväntas med tanke på uppdateringsintervallet.
- Efter fördröjning i tre timmar uppdateras cachen igen.
Konsumenter kan under samma process be om bilderna från IMemoryCache, men CacheWorker ansvarar för att uppdatera cacheminnet.
Hybridcache
Biblioteket HybridCache kombinerar fördelarna med minnesintern och distribuerad cachelagring samtidigt som du hanterar vanliga utmaningar med befintliga cachelagrings-API:er. I .NET 9 HybridCache finns ett enhetligt API som förenklar implementeringen av cachelagring och innehåller inbyggda funktioner som stampede-skydd och konfigurerbar serialisering.
Viktiga funktioner
HybridCache erbjuder flera fördelar jämfört med att använda IMemoryCache och IDistributedCache separat:
- Cachelagring på två nivåer: Hanterar automatiskt både minnesinterna (L1) och distribuerade cachelager (L2). Data hämtas från minnesintern cache först för hastighet, sedan från distribuerad cache om det behövs och slutligen från källan.
- Stampede-skydd: Förhindrar att flera samtidiga begäranden kör samma resurskrävande operation. Endast en begäran hämtar data medan andra väntar på resultatet.
- Konfigurerbar serialisering: Stöder flera serialiseringsformat, inklusive JSON (standard), protobuf och XML.
- Taggbaserad ogiltighet: Grupperar relaterade cacheposter med taggar för effektiv batch-ogiltighet.
-
Förenklat API: Metoden
GetOrCreateAsynchanterar cachemissar, serialisering och lagring automatiskt.
När du ska använda HybridCache
Överväg att använda HybridCache när:
- Du behöver både lokal (minnesintern) och distribuerad cachelagring i en miljö med flera servrar.
- Du vill ha skydd mot cachestampede-scenario.
- Du föredrar ett förenklat API framför manuellt samordning
IMemoryCacheochIDistributedCache. - Du behöver taggbaserad cache-invalidering för relaterade objekt.
Tips/Råd
För program med en server med enkla cachelagringsbehov kan minnesintern cachelagring vara tillräcklig. För program med flera servrar utan behov av stampede-skydd eller taggbaserad ogiltighet bör du överväga distribuerad cachelagring.
HybridCache-konfiguration
Om du vill använda HybridCacheinstallerar du NuGet-paketet Microsoft.Extensions.Caching.Hybrid :
dotnet add package Microsoft.Extensions.Caching.Hybrid
Registrera tjänsten HybridCache med DI genom att anropa AddHybridCache:
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHybridCache();
Föregående kod registrerar HybridCache med standardalternativ. Du kan också konfigurera globala alternativ:
var builderWithOptions = Host.CreateApplicationBuilder(args);
builderWithOptions.Services.AddHybridCache(options =>
{
options.MaximumPayloadBytes = 1024 * 1024; // 1 MB
options.MaximumKeyLength = 1024;
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(5),
LocalCacheExpiration = TimeSpan.FromMinutes(2)
};
});
Grundläggande användning
Den primära metoden för att interagera med HybridCache är GetOrCreateAsync. Den här metoden kontrollerar cachen efter en post med den angivna nyckeln och anropar, om den inte hittas, fabriksmetoden för att hämta data:
async Task<WeatherData> GetWeatherDataAsync(HybridCache cache, string city)
{
return await cache.GetOrCreateAsync(
$"weather:{city}",
async cancellationToken =>
{
// Simulate fetching from an external API
await Task.Delay(100, cancellationToken);
return new WeatherData(city, 72, "Sunny");
}
);
}
I föregående C#-kod:
- Metoden
GetOrCreateAsynctar en unik nyckel och en fabriksmetod. - Om data inte finns i cacheminnet anropas fabriksmetoden för att hämta dem.
- Data lagras automatiskt i både minnesintern och distribuerade cacheminnen.
- Endast en samtidig begäran kör fabriksmetoden; andra väntar på resultatet.
Alternativ för inmatning
Du kan åsidosätta globala standardvärden för specifika cacheposter med hjälp av HybridCacheEntryOptions:
async Task<WeatherData> GetWeatherWithOptionsAsync(HybridCache cache, string city)
{
var entryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(10),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
return await cache.GetOrCreateAsync(
$"weather:{city}",
async cancellationToken => new WeatherData(city, 72, "Sunny"),
entryOptions
);
}
Med inmatningsalternativen kan du konfigurera:
- HybridCacheEntryOptions.Expiration: Hur länge posten ska cachelagras i den distribuerade cachen.
- HybridCacheEntryOptions.LocalCacheExpiration: Hur länge posten ska cachelagras i lokalt minne.
- HybridCacheEntryOptions.Flags: Ytterligare flaggor för att kontrollera cachebeteendet.
Taggbaserad ogiltighet
Med taggar kan du gruppera relaterade cacheposter och ogiltigförklara dem tillsammans. Detta är användbart för scenarier där relaterade data måste uppdateras som en enhet:
async Task<CustomerData> GetCustomerAsync(HybridCache cache, int customerId)
{
var tags = new[] { "customer", $"customer:{customerId}" };
return await cache.GetOrCreateAsync(
$"customer:{customerId}",
async cancellationToken => new CustomerData(customerId, "John Doe", "john@example.com"),
new HybridCacheEntryOptions { Expiration = TimeSpan.FromMinutes(30) },
tags
);
}
Så här ogiltigförklarar du alla poster med en specifik tagg:
async Task InvalidateCustomerCacheAsync(HybridCache cache, int customerId)
{
await cache.RemoveByTagAsync($"customer:{customerId}");
}
Du kan också ogiltigförklara flera taggar samtidigt:
async Task InvalidateAllCustomersAsync(HybridCache cache)
{
await cache.RemoveByTagAsync(new[] { "customer", "orders" });
}
Anmärkning
Taggbaserad ogiltighet är en logisk åtgärd. Den tar inte aktivt bort värden från cachen, men ser till att taggade poster behandlas som cachemissar. Posterna upphör så småningom att gälla baserat på deras konfigurerade livslängd.
Ta bort cacheposter
Om du vill ta bort en specifik cachepost efter nyckel använder du RemoveAsync metoden:
async Task RemoveWeatherDataAsync(HybridCache cache, string city)
{
await cache.RemoveAsync($"weather:{city}");
}
Om du vill ogiltigförklara alla cachelagrade poster använder du det reserverade jokertecknet "*":
async Task InvalidateAllCacheAsync(HybridCache cache)
{
await cache.RemoveByTagAsync("*");
}
Serialisering
För scenarier HybridCache med distribuerad cachelagring kräver serialisering. Som standard hanterar den string och byte[] internt och använder System.Text.Json för andra typer. Du kan konfigurera anpassade serialiserare för specifika typer eller använda en general-purpose serializer:
// Custom serialization example
// Note: This requires implementing a custom IHybridCacheSerializer<T>
var builderWithSerializer = Host.CreateApplicationBuilder(args);
builderWithSerializer.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(10),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
// To add a custom serializer, uncomment and provide your implementation:
// .AddSerializer<WeatherData, CustomWeatherDataSerializer>();
Konfigurera distribuerad cache
HybridCache använder den konfigurerade IDistributedCache implementeringen för dess distribuerade cacheminne (L2). Även utan att ett IDistributedCache är konfigurerat, erbjuder HybridCache fortfarande minnesintern cachelagring och stampede-skydd. Så här lägger du till Redis som en distribuerad cache:
// Distributed cache with Redis
var builderWithRedis = Host.CreateApplicationBuilder(args);
builderWithRedis.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
builderWithRedis.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(30),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
});
Mer information om implementeringar av distribuerad cache finns i Distribuerad cachelagring.
Distribuerad cachelagring
I vissa fall krävs en distribuerad cache – så är fallet med flera appservrar. En distribuerad cache stöder högre utskalning än cachelagring i minnet. Om du använder en distribuerad cache avlastas cacheminnet till en extern process, men kräver extra nätverks-I/O och ger lite mer svarstid (även om det är nominellt).
De distribuerade cachelagringsabstraktionerna är en del av Microsoft.Extensions.Caching.Memory NuGet-paketet, och det finns till och med en AddDistributedMemoryCache tilläggsmetod.
Försiktighet
AddDistributedMemoryCache bör endast användas i utvecklings- eller testscenarier och är inte en livskraftig produktionsimplementering.
Överväg någon av de tillgängliga implementeringarna av IDistributedCache från följande paket:
Microsoft.Extensions.Caching.SqlServerMicrosoft.Extensions.Caching.StackExchangeRedisNCache.Microsoft.Extensions.Caching.OpenSource
API för distribuerad cachelagring
API:erna för distribuerad cachelagring är lite mer primitiva än deras api-motsvarigheter för cachelagring i minnet. Nyckel/värde-paren är lite mer grundläggande. Nycklar för cachelagring i minnet baseras på en object, medan distribuerade nycklar är en string. Med internminnescachelagring kan värdet vara vilken som helst starkt typad generisk typ, medan värden i den distribuerade cachelagringen sparas som byte[]. Det betyder inte att olika implementeringar inte exponerar starkt skrivna generiska värden, men det är en implementeringsinformation.
Skapa värden
Om du vill skapa värden i den distribuerade cachen anropar du något av de angivna API:erna:
AlphabetLetter Med hjälp av posten från cacheexemplet i minnet kan du serialisera objektet till JSON och sedan koda som string :byte[]
DistributedCacheEntryOptions options = new()
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMilliseconds(MillisecondsAbsoluteExpiration)
};
AlphabetLetter alphabetLetter = new(letter);
string json = JsonSerializer.Serialize(alphabetLetter);
byte[] bytes = Encoding.UTF8.GetBytes(json);
await cache.SetAsync(letter.ToString(), bytes, options);
Precis som minnescache kan cacheobjekt ha alternativ för att finjustera deras existens i cacheminnet, vilket i det här fallet innebär DistributedCacheEntryOptions.
Skapa tilläggsmetoder
Det finns flera bekvämlighetsbaserade tilläggsmetoder för att skapa värden. Dessa metoder hjälper till att undvika att koda string representationer av ett objekt i en byte[]:
Läsa värden
Om du vill läsa värden från den distribuerade cachen anropar du något av API:erna Get :
AlphabetLetter? alphabetLetter = null;
byte[]? bytes = await cache.GetAsync(letter.ToString());
if (bytes is { Length: > 0 })
{
string json = Encoding.UTF8.GetString(bytes);
alphabetLetter = JsonSerializer.Deserialize<AlphabetLetter>(json);
}
När en cachepost har lästs ut ur cacheminnet kan du hämta UTF8-kodad string representation från byte[].
Metoder för lästillägg
Det finns flera bekvämlighetsbaserade tilläggsmetoder för att läsa värden. Dessa metoder hjälper till att undvika avkodning byte[] i string representationer av objekt:
Uppdatera värden
Det går inte att uppdatera värdena i den distribuerade cachen med ett enda API-anrop. I stället kan värden återställa sina glidande förfallodatum med något av uppdaterings-API:erna:
Om det faktiska värdet måste uppdateras måste du ta bort värdet och sedan lägga till det igen.
Ta bort värden
Om du vill ta bort värden i den distribuerade cachen anropar du något av API:erna Remove :
Tips/Råd
Det finns synkrona versioner av dessa API:er, men tänk på att implementeringar av distribuerade cacheminnen är beroende av nätverks-I/O. Därför är det vanligtvis bättre att använda asynkrona API:er.