Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Von Rick Anderson, John Luo und Steve Smith
Das Zwischenspeichern kann die Leistung und Skalierbarkeit einer App erheblich verbessern, indem die zum Generieren von Inhalten erforderliche Arbeit reduziert wird. Das Zwischenspeichern funktioniert am besten mit Daten, die sich selten ändern und teuer zu generieren sind. Durch das Zwischenspeichern wird eine Kopie von Daten erstellt, die viel schneller als von der Quelle zurückgegeben werden können. Apps sollten geschrieben und getestet werden, um niemals von zwischengespeicherten Daten abhängig zu sein.
ASP.NET Core unterstützt mehrere verschiedene Caches. Der einfachste Cache basiert auf dem IMemoryCache.
IMemoryCache
stellt einen Cache dar, der im Arbeitsspeicher des Webservers gespeichert ist. Apps, die in einer Serverfarm (mit mehreren Servern) ausgeführt werden, sollten bei Verwendung des In-Memory-Cache sicherstellen, dass Sitzungen persistent sind (Sticky Sessions). Sticky Sessions stellen sicher, dass Anfragen eines Clients immer zum selben Server geleitet werden. Beispielsweise verwenden Azure Web Apps Anwendungsanforderungsrouting (ARR), um alle Anforderungen an denselben Server weiterzuleiten.
Nicht-sticky-Sitzungen in einer Webfarm erfordern einen verteilten Cache, um Cachekonsistenzprobleme zu vermeiden. Bei einigen Apps kann ein verteilter Cache eine höhere Skalierung als ein Speichercache unterstützen. Durch die Verwendung eines verteilten Caches wird der Cachespeicher in einen externen Prozess entladen.
Der Speichercache kann jedes Objekt speichern. Die verteilte Cacheschnittstelle ist beschränkt auf byte[]
. Die Cacheelemente im Arbeitsspeicher und im verteilten Cache werden als Schlüsselwertpaare gespeichert.
System.Runtime.Caching/MemoryCache
System.Runtime.Caching / MemoryCache (NuGet package) kann mit folgenden Komponenten verwendet werden:
- .NET Standard 2.0 oder höher.
- Jede .NET-Implementierung , die auf .NET Standard 2.0 oder höher ausgerichtet ist. Beispiel: ASP.NET Core 3.1 oder höher.
- .NET Framework 4.5 oder höher.
Microsoft.Extensions.Caching.Memory/IMemoryCache
(in diesem Artikel beschrieben) wird gegenüber System.Runtime.Caching
/MemoryCache
empfohlen, da es besser in ASP.NET Core integriert ist. Beispielsweise funktioniert IMemoryCache
nativ mit der Dependency Injection (Abhängigkeitsinjektion) in ASP.NET Core.
Verwenden Sie System.Runtime.Caching
/MemoryCache
beim Portieren von Code von ASP.NET 4.x zu ASP.NET Core als Kompatibilitätsbrücke.
Cacherichtlinien
- Code sollte immer eine Fallbackoption zum Abrufen von Daten haben und nicht von einem zwischengespeicherten Wert abhängen, der verfügbar ist.
- Der Cache verwendet eine knappe Ressource, Arbeitsspeicher. Cachewachstum begrenzen:
- Fügen Sie keine externe Eingabe in den Cache ein. Die Verwendung beliebiger vom Benutzer bereitgestellter Eingaben als Cacheschlüssel wird beispielsweise nicht empfohlen, da die Eingabe möglicherweise eine unvorhersehbare Menge an Arbeitsspeicher belegt.
- Verwenden Sie Ablauffristen, um das Cachewachstum einzuschränken.
- Verwenden Sie "SetSize", "Size" und "SizeLimit", um die Cachegröße zu begrenzen. Die ASP.NET Core-Runtime beschränkt die Cachegröße nicht auf der Grundlage des Arbeitsspeicherdrucks. Es liegt an dem Entwickler, die Cachegröße einzuschränken.
Verwenden von IMemoryCache
Warnung
Die Verwendung eines freigegebenen Speichercache über Dependency Injection und das Aufrufen von SetSize
, Size
oder SizeLimit
zum Einschränken der Cachegröße kann dazu führen, dass die App fehlschlägt. Wenn für einen Cache eine Größenbeschränkung festgelegt wird, müssen alle Einträge beim Hinzufügen eine Größe angeben. Dies kann zu Problemen führen, da Entwickler möglicherweise nicht vollzugriff auf die Verwendung des freigegebenen Caches haben.
Bei der Verwendung von SetSize
, Size
oder SizeLimit
zur Begrenzung des Cache, erstellen Sie ein Cache-Singleton für die Zwischenspeicherung. Weitere Informationen und ein Beispiel finden Sie unter "SetSize", "Size" und "SizeLimit", um die Cachegröße zu begrenzen.
Ein freigegebener Cache ist ein Cache, der auch von anderen Frameworks oder Bibliotheken genutzt wird.
In-Memory-Caching ist ein Dienst, der von einer App mittels Dependency Injection angesprochen wird. Fordern Sie die IMemoryCache
Instanz im Konstruktor an:
public class IndexModel : PageModel
{
private readonly IMemoryCache _memoryCache;
public IndexModel(IMemoryCache memoryCache) =>
_memoryCache = memoryCache;
// ...
Der folgende Code verwendet TryGetValue , um zu überprüfen, ob sich eine Zeit im Cache befindet. Falls ein Zeitpunkt nicht zwischengespeichert ist, wird ein neuer Eintrag erstellt und dem Cache mit Set hinzugefügt.
public void OnGet()
{
CurrentDateTime = DateTime.Now;
if (!_memoryCache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
{
cacheValue = CurrentDateTime;
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(3));
_memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
}
CacheCurrentDateTime = cacheValue;
}
Im vorherigen Code wird der Cacheeintrag mit einem gleitenden Ablauf von drei Sekunden konfiguriert. Wenn der Cacheeintrag für mehr als drei Sekunden nicht aufgerufen wird, wird er aus dem Cache entfernt. Jedes Mal, wenn auf den Cacheeintrag zugegriffen wird, verbleibt er für weitere 3 Sekunden im Cache. Die CacheKeys
Klasse ist Teil des Downloadbeispiels.
Die aktuelle Uhrzeit und die zwischengespeicherte Uhrzeit werden angezeigt:
<ul>
<li>Current Time: @Model.CurrentDateTime</li>
<li>Cached Time: @Model.CacheCurrentDateTime</li>
</ul>
Der folgende Code verwendet die Set
Erweiterungsmethode, um Daten für eine relative Zeit zwischenzuspeichern, ohne MemoryCacheEntryOptions
:
_memoryCache.Set(CacheKeys.Entry, DateTime.Now, TimeSpan.FromDays(1));
Im vorherigen Code wird der Cacheeintrag mit einem relativen Ablauf von einem Tag konfiguriert. Der Cacheeintrag wird nach einem Tag aus dem Cache entfernt, auch wenn innerhalb dieses Timeoutzeitraums darauf zugegriffen wird.
Der folgende Code verwendet GetOrCreate und GetOrCreateAsync zum Zwischenspeichern von Daten.
public void OnGetCacheGetOrCreate()
{
var cachedValue = _memoryCache.GetOrCreate(
CacheKeys.Entry,
cacheEntry =>
{
cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
return DateTime.Now;
});
// ...
}
public async Task OnGetCacheGetOrCreateAsync()
{
var cachedValue = await _memoryCache.GetOrCreateAsync(
CacheKeys.Entry,
cacheEntry =>
{
cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
return Task.FromResult(DateTime.Now);
});
// ...
}
Der folgende Code ruft zum Abrufen der zwischengespeicherten Zeit auf Get :
var cacheEntry = _memoryCache.Get<DateTime?>(CacheKeys.Entry);
Mit dem folgenden Code wird ein zwischengespeichertes Element mit absoluter Ablaufzeit abgerufen oder erstellt.
var cachedValue = _memoryCache.GetOrCreate(
CacheKeys.Entry,
cacheEntry =>
{
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
return DateTime.Now;
});
Bei einem Satz zwischengespeicherter Elemente, für den nur eine gleitende Ablaufzeit festgelegt ist, besteht das Risiko, dass er nie abläuft. Wenn innerhalb des gleitenden Ablaufintervalls wiederholt auf das zwischengespeicherte Element zugegriffen wird, läuft das Element nie ab. Kombinieren Sie eine gleitende Ablaufzeit mit einem absoluten Ablaufdatum, um das Ablaufen des Elements zu gewährleisten. Der absolute Verfall legt eine obere Grenze fest, wie lange das Element zwischengespeichert werden kann, wobei das Element noch früher verfallen kann, wenn es nicht innerhalb des gleitenden Verfallsintervalls angefordert wird. Wenn entweder das gleitende Ablaufintervall oder die absolute Ablaufzeit verstrichen ist, wird das Element aus dem Cache entfernt.
Mit dem folgenden Code wird ein zwischengespeichertes Element mit gleitender und absoluter Ablaufzeit abgerufen oder erstellt:
var cachedValue = _memoryCache.GetOrCreate(
CacheKeys.CallbackEntry,
cacheEntry =>
{
cacheEntry.SlidingExpiration = TimeSpan.FromSeconds(3);
cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
return DateTime.Now;
});
Der vorstehende Code garantiert, dass die Daten nicht länger als die absolute Zeit zwischengespeichert werden.
GetOrCreate, GetOrCreateAsyncund Get sind Erweiterungsmethoden in der CacheExtensions Klasse. Diese Methoden erweitern die Funktion von IMemoryCache.
MemoryCacheEntryOptions
Das folgende Beispiel:
- Legt die Cachepriorität auf CacheItemPriority.NeverRemove.
- Legt einen PostEvictionDelegate Wert fest, der aufgerufen wird, nachdem der Eintrag aus dem Cache entfernt wurde. Der Rückruf wird in einem anderen Thread als dem Code ausgeführt, der das Element aus dem Cache entfernt.
public void OnGetCacheRegisterPostEvictionCallback()
{
var memoryCacheEntryOptions = new MemoryCacheEntryOptions()
.SetPriority(CacheItemPriority.NeverRemove)
.RegisterPostEvictionCallback(PostEvictionCallback, _memoryCache);
_memoryCache.Set(CacheKeys.CallbackEntry, DateTime.Now, memoryCacheEntryOptions);
}
private static void PostEvictionCallback(
object cacheKey, object cacheValue, EvictionReason evictionReason, object state)
{
var memoryCache = (IMemoryCache)state;
memoryCache.Set(
CacheKeys.CallbackMessage,
$"Entry {cacheKey} was evicted: {evictionReason}.");
}
Verwenden von SetSize, Size und SizeLimit zum Einschränken der Cachegröße
Eine MemoryCache
Instanz kann optional eine Größenbeschränkung angeben und erzwingen. Die Größenbeschränkung für den Cache verfügt nicht über eine definierte Maßeinheit, da der Cache keinen Mechanismus zum Messen der Größe von Einträgen hat. Wenn der Grenzwert für die Cachegröße festgelegt ist, müssen alle Einträge die Größe angeben. Die ASP.NET Core-Runtime beschränkt die Cachegröße nicht auf der Grundlage des Arbeitsspeicherdrucks. Es liegt an dem Entwickler, die Cachegröße einzuschränken. Die angegebene Größe befindet sich in Einheiten, die der Entwickler auswäht.
Beispiel:
- Wenn die Web-App in erster Linie Zeichenfolgen zwischenspeichert, kann jede Cacheeintragsgröße die Länge der Zeichenfolge sein.
- Die App kann die Größe aller Einträge als 1 angeben, und die Größenbeschränkung ist die Anzahl der Einträge.
Ist SizeLimit dies nicht festgelegt, wächst der Cache ohne Grenzen. Die ASP.NET Core-Runtime schneidet den Cache nicht ab, wenn der Systemspeicher niedrig ist. Apps müssen für Folgendes entwickelt werden:
- Schränken Sie das Cachewachstum ein.
- Rufen Sie Compact oder Remove an, wenn der verfügbare Arbeitsspeicher begrenzt ist.
Der folgende Code erstellt eine einheitlose feste Größe MemoryCache , auf die durch Abhängigkeitseinfügung zugegriffen werden kann:
public class MyMemoryCache
{
public MemoryCache Cache { get; } = new MemoryCache(
new MemoryCacheOptions
{
SizeLimit = 1024
});
}
SizeLimit
enthält keine Einheiten. Zwischengespeicherte Einträge müssen die Größe in allen Einheiten angeben, die sie für am besten geeignet halten, wenn der Grenzwert für die Cachegröße festgelegt wurde. Alle Benutzer einer Cacheinstanz sollten dasselbe Einheitensystem verwenden. Ein Eintrag wird nicht zwischengespeichert, wenn die Summe der zwischengespeicherten Eintragsgrößen den durch SizeLimit
angegebenen Wert überschreitet. Wenn kein Grenzwert für die Cachegröße festgelegt ist, wird die für den Eintrag festgelegte Cachegröße ignoriert.
Der folgende Code registriert MyMemoryCache
beim Dependency Injection-Container:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddSingleton<MyMemoryCache>();
MyMemoryCache
wird als unabhängiger Speichercache für Komponenten erstellt, die diesen größengrenzten Cache kennen und wissen, wie die Größe des Cacheeintrags entsprechend festgelegt wird.
Die Größe des Cacheeintrags kann mithilfe der SetSize Erweiterungsmethode oder der Size Eigenschaft festgelegt werden:
if (!_myMemoryCache.Cache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue))
{
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSize(1);
// cacheEntryOptions.Size = 1;
_myMemoryCache.Cache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions);
}
Im vorherigen Code erzielen die beiden hervorgehobenen Zeilen das gleiche Ergebnis, indem die Größe des Cacheeintrags festgelegt wird.
SetSize
wird zur Vereinfachung beim Verketten von Aufrufen auf new MemoryCacheOptions()
bereitgestellt.
MemoryCache.Kompakt
MemoryCache.Compact
versucht, den angegebenen Prozentsatz des Caches in der folgenden Reihenfolge zu entfernen:
- Alle abgelaufenen Artikel.
- Elemente nach Priorität. Elemente mit der niedrigsten Priorität werden zuerst entfernt.
- Am wenigsten kürzlich gebrauchte Objekte.
- Artikel mit dem frühestem absoluten Verfallsdatum.
- Elemente mit dem frühesten gleitenden Verfallsdatum.
Angeheftete Elemente mit Priorität NeverRemove werden nie entfernt. Der folgende Code entfernt ein Cacheelement und ruft Compact
auf, um 25% zwischengespeicherte Einträge zu entfernen.
_myMemoryCache.Cache.Remove(CacheKeys.Entry);
_myMemoryCache.Cache.Compact(.25);
Weitere Informationen finden Sie in der Compact-Quelle auf GitHub.
Cacheabhängigkeiten
Das folgende Beispiel zeigt, wie ein Cacheeintrag abläuft, wenn ein abhängiger Eintrag abläuft. Dem zwischengespeicherten Element wird ein CancellationChangeToken hinzugefügt. Wenn Cancel
auf CancellationTokenSource
aufgerufen wird, werden beide Cacheeinträge gelöscht.
public void OnGetCacheCreateDependent()
{
var cancellationTokenSource = new CancellationTokenSource();
_memoryCache.Set(
CacheKeys.DependentCancellationTokenSource,
cancellationTokenSource);
using var parentCacheEntry = _memoryCache.CreateEntry(CacheKeys.Parent);
parentCacheEntry.Value = DateTime.Now;
_memoryCache.Set(
CacheKeys.Child,
DateTime.Now,
new CancellationChangeToken(cancellationTokenSource.Token));
}
public void OnGetCacheRemoveDependent()
{
var cancellationTokenSource = _memoryCache.Get<CancellationTokenSource>(
CacheKeys.DependentCancellationTokenSource);
cancellationTokenSource.Cancel();
}
Durch die Verwendung eines CancellationTokenSource können mehrere Cacheeinträge als Gruppe entfernt werden. Mit dem using
Muster im obigen Code erben Cacheeinträge, die innerhalb des using
Bereichs erstellt wurden, Trigger und Ablaufeinstellungen.
Zusätzliche Hinweise
Der Ablauf erfolgt nicht im Hintergrund. Es gibt keinen Timer, der den Cache aktiv auf abgelaufene Elemente überprüft. Jede Aktivität im Cache (
Get
,TryGetValue
,Set
,Remove
) kann eine Hintergrundüberprüfung für abgelaufene Elemente auslösen. Ein Timer auf demCancellationTokenSource
(CancelAfter) entfernt auch den Eintrag und löst eine Suche nach abgelaufenen Elementen aus. Im folgenden Beispiel wird CancellationTokenSource(TimeSpan) für das registrierte Token verwendet. Durch Auslösen dieses Tokens wird der Eintrag sofort entfernt, und die Rückrufe für den Entfernungsvorgang werden ausgelöst:if (!_memoryCache.TryGetValue(CacheKeys.Entry, out DateTime cacheValue)) { cacheValue = DateTime.Now; var cancellationTokenSource = new CancellationTokenSource( TimeSpan.FromSeconds(10)); var cacheEntryOptions = new MemoryCacheEntryOptions() .AddExpirationToken( new CancellationChangeToken(cancellationTokenSource.Token)) .RegisterPostEvictionCallback((key, value, reason, state) => { ((CancellationTokenSource)state).Dispose(); }, cancellationTokenSource); _memoryCache.Set(CacheKeys.Entry, cacheValue, cacheEntryOptions); }
Bei Verwendung eines Rückrufs zum Erneuten Auffüllen eines Cacheelements:
- Mehrere Anfragen können den zwischengespeicherten Schlüsselwert möglicherweise leer vorfinden, da der Callback noch nicht abgeschlossen ist.
- Dies kann dazu führen, dass mehrere Threads das zwischengespeicherte Element erneut auffüllen.
Wenn ein Cacheeintrag zum Erstellen eines anderen verwendet wird, kopiert der untergeordnete Eintrag die Ablauftoken und zeitbasierten Ablaufeinstellungen des übergeordneten Eintrags. Der untergeordnete Eintrag läuft nicht durch manuelles Entfernen oder Aktualisieren des übergeordneten Eintrags ab.
Verwenden Sie PostEvictionCallbacks, um die Rückrufe festzulegen, die ausgelöst werden, nachdem der Eintrag aus dem Cache entfernt wurde.
Für die meisten Apps ist
IMemoryCache
aktiviert. Beispielsweise wirdAddMvc
durch Aufrufen vonAddControllersWithViews
,AddRazorPages
,AddMvcCore().AddRazorViewEngine
,Add{Service}
und vielen anderenProgram.cs
-Methoden inIMemoryCache
aktiviert. Für Apps, die keine der vorherigenAdd{Service}
-Methoden aufrufen, kann es erforderlich sein, AddMemoryCache inProgram.cs
aufzurufen.
Aktualisierung des Hintergrundcaches
Verwenden Sie einen Hintergrunddienst wie IHostedService zum Aktualisieren des Cache. Der Hintergrunddienst kann die Einträge neu berechnen und sie dann nur im Cache speichern, wenn sie bereit sind.
Weitere Ressourcen
- Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
- Verteiltes Zwischenspeichern in ASP.NET Core
- Erkennen von Änderungen mit Änderungstoken in ASP.NET Core
- Zwischenspeichern von Antworten in ASP.NET Core
- Middleware für Response-Caching in ASP.NET Core
- Cachetag-Hilfsprogramm in ASP.NET Core MVC
- Hilfsprogramm für verteilte Cachetags in ASP.NET Core
Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)
Grundlagen zum Zwischenspeichern
Das Zwischenspeichern kann die Leistung und Skalierbarkeit einer App erheblich verbessern, indem die zum Generieren von Inhalten erforderliche Arbeit reduziert wird. Das Zwischenspeichern funktioniert am besten mit Daten, die sich selten ändern und teuer zu generieren sind. Durch das Zwischenspeichern wird eine Kopie von Daten erstellt, die viel schneller als von der Quelle zurückgegeben werden können. Apps sollten geschrieben und getestet werden, um niemals von zwischengespeicherten Daten abhängig zu sein.
ASP.NET Core unterstützt mehrere verschiedene Caches. Der einfachste Cache basiert auf dem IMemoryCache.
IMemoryCache
stellt einen Cache dar, der im Arbeitsspeicher des Webservers gespeichert ist. Apps, die in einer Serverfarm (mit mehreren Servern) ausgeführt werden, sollten bei Verwendung des In-Memory-Cache sicherstellen, dass Sitzungen persistent sind (Sticky Sessions). Bei persistenten Sitzungen wird sichergestellt, dass nachfolgende Anforderungen von einem Client immer an denselben Server gesendet werden. Beispielsweise verwenden Azure Web Apps Anwendungsanforderungsrouting (ARR), um alle nachfolgenden Anforderungen an denselben Server weiterzuleiten.
Nicht-sticky-Sitzungen in einer Webfarm erfordern einen verteilten Cache, um Cachekonsistenzprobleme zu vermeiden. Bei einigen Apps kann ein verteilter Cache eine höhere Skalierung als ein Speichercache unterstützen. Durch die Verwendung eines verteilten Caches wird der Cachespeicher in einen externen Prozess entladen.
Der Speichercache kann jedes Objekt speichern. Die verteilte Cacheschnittstelle ist beschränkt auf byte[]
. Die Cacheelemente im Arbeitsspeicher und im verteilten Cache werden als Schlüsselwertpaare gespeichert.
System.Runtime.Caching/MemoryCache
System.Runtime.Caching / MemoryCache (NuGet package) kann mit folgenden Komponenten verwendet werden:
- .NET Standard 2.0 oder höher.
- Jede .NET-Implementierung , die auf .NET Standard 2.0 oder höher ausgerichtet ist. Beispiel: ASP.NET Core 3.1 oder höher.
- .NET Framework 4.5 oder höher.
Microsoft.Extensions.Caching.Memory/IMemoryCache
(in diesem Artikel beschrieben) wird gegenüber System.Runtime.Caching
/MemoryCache
empfohlen, da es besser in ASP.NET Core integriert ist. Beispielsweise funktioniert IMemoryCache
nativ mit der Dependency Injection (Abhängigkeitsinjektion) in ASP.NET Core.
Verwenden Sie System.Runtime.Caching
/MemoryCache
beim Portieren von Code von ASP.NET 4.x zu ASP.NET Core als Kompatibilitätsbrücke.
Cacherichtlinien
- Code sollte immer eine Fallbackoption zum Abrufen von Daten haben und nicht von einem zwischengespeicherten Wert abhängen, der verfügbar ist.
- Der Cache verwendet eine knappe Ressource, Arbeitsspeicher. Cachewachstum begrenzen:
- Verwenden Sie keine externe Eingabe als Cacheschlüssel.
- Verwenden Sie Ablauffristen, um das Cachewachstum einzuschränken.
- Verwenden Sie "SetSize", "Size" und "SizeLimit", um die Cachegröße zu begrenzen. Die ASP.NET Core-Runtime beschränkt die Cachegröße nicht auf der Grundlage des Arbeitsspeicherdrucks. Es liegt an dem Entwickler, die Cachegröße einzuschränken.
Verwenden von IMemoryCache
Warnung
Die Verwendung eines freigegebenen Speichercache über Dependency Injection und das Aufrufen von SetSize
, Size
oder SizeLimit
zum Einschränken der Cachegröße kann dazu führen, dass die App fehlschlägt. Wenn für einen Cache eine Größenbeschränkung festgelegt wird, müssen alle Einträge beim Hinzufügen eine Größe angeben. Dies kann zu Problemen führen, da Entwickler möglicherweise nicht vollzugriff auf die Verwendung des freigegebenen Caches haben.
Bei der Verwendung von SetSize
, Size
oder SizeLimit
zur Begrenzung des Cache, erstellen Sie ein Cache-Singleton für die Zwischenspeicherung. Weitere Informationen und ein Beispiel finden Sie unter "SetSize", "Size" und "SizeLimit", um die Cachegröße zu begrenzen.
Ein freigegebener Cache ist ein Cache, der auch von anderen Frameworks oder Bibliotheken genutzt wird.
In-Memory-Caching ist ein Dienst, der von einer App mittels Dependency Injection angesprochen wird. Fordern Sie die IMemoryCache
Instanz im Konstruktor an:
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
Der folgende Code verwendet TryGetValue , um zu überprüfen, ob sich eine Zeit im Cache befindet. Wenn ein Zeitpunkt nicht zwischengespeichert ist, wird ein neuer Eintrag erstellt und dem Cache hinzugefügt.Set Die CacheKeys
Klasse ist Teil des Downloadbeispiels.
public static class CacheKeys
{
public static string Entry => "_Entry";
public static string CallbackEntry => "_Callback";
public static string CallbackMessage => "_CallbackMessage";
public static string Parent => "_Parent";
public static string Child => "_Child";
public static string DependentMessage => "_DependentMessage";
public static string DependentCTS => "_DependentCTS";
public static string Ticks => "_Ticks";
public static string CancelMsg => "_CancelMsg";
public static string CancelTokenSource => "_CancelTokenSource";
}
public IActionResult CacheTryGetValueSet()
{
DateTime cacheEntry;
// Look for cache key.
if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now;
// Set cache options.
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds(3));
// Save data in cache.
_cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions);
}
return View("Cache", cacheEntry);
}
Die aktuelle Uhrzeit und die zwischengespeicherte Uhrzeit werden angezeigt:
@model DateTime?
<div>
<h2>Actions</h2>
<ul>
<li><a asp-controller="Home" asp-action="CacheTryGetValueSet">TryGetValue and Set</a></li>
<li><a asp-controller="Home" asp-action="CacheGet">Get</a></li>
<li><a asp-controller="Home" asp-action="CacheGetOrCreate">GetOrCreate</a></li>
<li><a asp-controller="Home" asp-action="CacheGetOrCreateAsynchronous">CacheGetOrCreateAsynchronous</a></li>
<li><a asp-controller="Home" asp-action="CacheRemove">Remove</a></li>
<li><a asp-controller="Home" asp-action="CacheGetOrCreateAbs">CacheGetOrCreateAbs</a></li>
<li><a asp-controller="Home" asp-action="CacheGetOrCreateAbsSliding">CacheGetOrCreateAbsSliding</a></li>
</ul>
</div>
<h3>Current Time: @DateTime.Now.TimeOfDay.ToString()</h3>
<h3>Cached Time: @(Model == null ? "No cached entry found" : Model.Value.TimeOfDay.ToString())</h3>
Der folgende Code verwendet die Set
Erweiterungsmethode, um Daten für eine relative Zeit zwischenzuspeichern, ohne das MemoryCacheEntryOptions
Objekt zu erstellen:
public IActionResult SetCacheRelativeExpiration()
{
DateTime cacheEntry;
// Look for cache key.
if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now;
// Save data in cache and set the relative expiration time to one day
_cache.Set(CacheKeys.Entry, cacheEntry, TimeSpan.FromDays(1));
}
return View("Cache", cacheEntry);
}
Der zwischengespeicherte DateTime
Wert verbleibt im Cache, solange innerhalb des Timeout-Zeitraums Anforderungen vorhanden sind.
Der folgende Code verwendet GetOrCreate und GetOrCreateAsync zum Zwischenspeichern von Daten.
public IActionResult CacheGetOrCreate()
{
var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return DateTime.Now;
});
return View("Cache", cacheEntry);
}
public async Task<IActionResult> CacheGetOrCreateAsynchronous()
{
var cacheEntry = await
_cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
{
entry.SlidingExpiration = TimeSpan.FromSeconds(3);
return Task.FromResult(DateTime.Now);
});
return View("Cache", cacheEntry);
}
Der folgende Code ruft zum Abrufen der zwischengespeicherten Zeit auf Get :
public IActionResult CacheGet()
{
var cacheEntry = _cache.Get<DateTime?>(CacheKeys.Entry);
return View("Cache", cacheEntry);
}
Mit dem folgenden Code wird ein zwischengespeichertes Element mit absoluter Ablaufzeit abgerufen oder erstellt.
public IActionResult CacheGetOrCreateAbs()
{
var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
return DateTime.Now;
});
return View("Cache", cacheEntry);
}
Bei einem Satz zwischengespeicherter Elemente, für den nur eine gleitende Ablaufzeit festgelegt ist, besteht das Risiko, dass er nie abläuft. Wenn innerhalb des gleitenden Ablaufintervalls wiederholt auf das zwischengespeicherte Element zugegriffen wird, läuft das Element nie ab. Kombinieren Sie eine gleitende Ablaufzeit mit einem absoluten Ablaufdatum, um das Ablaufen des Elements zu gewährleisten. Der absolute Verfall legt eine obere Grenze fest, wie lange das Element zwischengespeichert werden kann, wobei das Element noch früher verfallen kann, wenn es nicht innerhalb des gleitenden Verfallsintervalls angefordert wird. Wenn entweder das gleitende Ablaufintervall oder die absolute Ablaufzeit verstrichen ist, wird das Element aus dem Cache entfernt.
Mit dem folgenden Code wird ein zwischengespeichertes Element mit gleitender und absoluter Ablaufzeit abgerufen oder erstellt:
public IActionResult CacheGetOrCreateAbsSliding()
{
var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
{
entry.SetSlidingExpiration(TimeSpan.FromSeconds(3));
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20);
return DateTime.Now;
});
return View("Cache", cacheEntry);
}
Der vorstehende Code garantiert, dass die Daten nicht länger als die absolute Zeit zwischengespeichert werden.
GetOrCreate, GetOrCreateAsyncund Get sind Erweiterungsmethoden in der CacheExtensions Klasse. Diese Methoden erweitern die Funktion von IMemoryCache.
MemoryCacheEntryOptions
Es folgen einige Beispielszenarien:
- Legt eine gleitende Ablaufzeit fest. Anforderungen, die auf dieses zwischengespeicherte Element zugreifen, setzen die gleitende Ablaufzeit zurück.
- Legt die Cachepriorität auf CacheItemPriority.NeverRemove.
- Legt einen PostEvictionDelegate Fest, der aufgerufen wird, nachdem der Eintrag aus dem Cache entfernt wurde. Der Rückruf wird in einem anderen Thread als dem Code ausgeführt, der das Element aus dem Cache entfernt.
public IActionResult CreateCallbackEntry()
{
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Pin to cache.
.SetPriority(CacheItemPriority.NeverRemove)
// Add eviction callback
.RegisterPostEvictionCallback(callback: EvictionCallback, state: this);
_cache.Set(CacheKeys.CallbackEntry, DateTime.Now, cacheEntryOptions);
return RedirectToAction("GetCallbackEntry");
}
public IActionResult GetCallbackEntry()
{
return View("Callback", new CallbackViewModel
{
CachedTime = _cache.Get<DateTime?>(CacheKeys.CallbackEntry),
Message = _cache.Get<string>(CacheKeys.CallbackMessage)
});
}
public IActionResult RemoveCallbackEntry()
{
_cache.Remove(CacheKeys.CallbackEntry);
return RedirectToAction("GetCallbackEntry");
}
private static void EvictionCallback(object key, object value,
EvictionReason reason, object state)
{
var message = $"Entry was evicted. Reason: {reason}.";
((HomeController)state)._cache.Set(CacheKeys.CallbackMessage, message);
}
Verwenden von SetSize, Size und SizeLimit zum Einschränken der Cachegröße
Eine MemoryCache
Instanz kann optional eine Größenbeschränkung angeben und erzwingen. Der Grenzwert für die Cachegröße verfügt nicht über eine definierte Maßeinheit, da der Cache keinen Mechanismus zum Messen der Größe von Einträgen hat. Wenn der Grenzwert für die Cachegröße festgelegt ist, müssen alle Einträge die Größe angeben. Die ASP.NET Core-Runtime beschränkt die Cachegröße nicht auf der Grundlage des Arbeitsspeicherdrucks. Es liegt an dem Entwickler, die Cachegröße einzuschränken. Die angegebene Größe befindet sich in Einheiten, die der Entwickler auswäht.
Beispiel:
- Wenn die Web-App in erster Linie Zeichenfolgen zwischenspeichert, kann jede Cacheeintragsgröße die Länge der Zeichenfolge sein.
- Die App kann die Größe aller Einträge als 1 angeben, und die Größenbeschränkung ist die Anzahl der Einträge.
Ist SizeLimit dies nicht festgelegt, wächst der Cache ohne Grenzen. Die ASP.NET Core-Runtime schneidet den Cache nicht ab, wenn der Systemspeicher niedrig ist. Apps müssen für Folgendes entwickelt werden:
- Schränken Sie das Cachewachstum ein.
- Rufen Sie Compact oder Remove an, wenn der verfügbare Arbeitsspeicher begrenzt ist.
Der folgende Code erstellt eine einheitlose feste Größe MemoryCache , auf die durch Abhängigkeitseinfügung zugegriffen werden kann:
// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache
{
public MemoryCache Cache { get; private set; }
public MyMemoryCache()
{
Cache = new MemoryCache(new MemoryCacheOptions
{
SizeLimit = 1024
});
}
}
SizeLimit
enthält keine Einheiten. Zwischengespeicherte Einträge müssen die Größe in allen Einheiten angeben, die sie als am besten geeignet erachten, wenn der Grenzwert für die Cachegröße festgelegt wurde. Alle Benutzer einer Cacheinstanz sollten dasselbe Einheitensystem verwenden. Ein Eintrag wird nicht zwischengespeichert, wenn die Summe der zwischengespeicherten Eintragsgrößen den angegebenen Wert von SizeLimit
überschreitet. Wenn keine Cachegrößenbeschränkung festgelegt ist, wird die für den Eintrag festgelegte Cachegröße ignoriert.
Der folgende Code registriert MyMemoryCache
beim Dependency Injection-Container.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSingleton<MyMemoryCache>();
}
MyMemoryCache
wird als unabhängiger Speichercache für Komponenten erstellt, die diesen größengrenzten Cache kennen und wissen, wie die Größe des Cacheeintrags entsprechend festgelegt wird.
Der folgende Code verwendet MyMemoryCache
:
public class SetSize : PageModel
{
private MemoryCache _cache;
public static readonly string MyKey = "_MyKey";
public SetSize(MyMemoryCache memoryCache)
{
_cache = memoryCache.Cache;
}
[TempData]
public string DateTime_Now { get; set; }
public IActionResult OnGet()
{
if (!_cache.TryGetValue(MyKey, out string cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now.TimeOfDay.ToString();
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Set cache entry size by extension method.
.SetSize(1)
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds(3));
// Set cache entry size via property.
// cacheEntryOptions.Size = 1;
// Save data in cache.
_cache.Set(MyKey, cacheEntry, cacheEntryOptions);
}
DateTime_Now = cacheEntry;
return RedirectToPage("./Index");
}
}
Die Größe des Cacheeintrags kann durch Size oder die SetSize Erweiterungsmethoden festgelegt werden.
public IActionResult OnGet()
{
if (!_cache.TryGetValue(MyKey, out string cacheEntry))
{
// Key not in cache, so get data.
cacheEntry = DateTime.Now.TimeOfDay.ToString();
var cacheEntryOptions = new MemoryCacheEntryOptions()
// Set cache entry size by extension method.
.SetSize(1)
// Keep in cache for this time, reset time if accessed.
.SetSlidingExpiration(TimeSpan.FromSeconds(3));
// Set cache entry size via property.
// cacheEntryOptions.Size = 1;
// Save data in cache.
_cache.Set(MyKey, cacheEntry, cacheEntryOptions);
}
DateTime_Now = cacheEntry;
return RedirectToPage("./Index");
}
MemoryCache.Kompakt
MemoryCache.Compact
versucht, den angegebenen Prozentsatz des Caches in der folgenden Reihenfolge zu entfernen:
- Alle abgelaufenen Artikel.
- Elemente nach Priorität. Elemente mit der niedrigsten Priorität werden zuerst entfernt.
- Am wenigsten kürzlich gebrauchte Objekte.
- Artikel mit dem frühestem absoluten Verfallsdatum.
- Elemente mit dem frühesten gleitenden Verfallsdatum.
Angeheftete Elemente mit Priorität NeverRemove werden nie entfernt. Der folgende Code entfernt ein Cacheelement und ruft Compact
auf.
_cache.Remove(MyKey);
// Remove 33% of cached items.
_cache.Compact(.33);
cache_size = _cache.Count;
Weitere Informationen finden Sie in der Compact-Quelle auf GitHub.
Cacheabhängigkeiten
Das folgende Beispiel zeigt, wie ein Cacheeintrag abläuft, wenn ein abhängiger Eintrag abläuft. Dem zwischengespeicherten Element wird ein CancellationChangeToken hinzugefügt. Wenn Cancel
auf dem CancellationTokenSource
aufgerufen wird, werden beide Cache-Einträge gelöscht.
public IActionResult CreateDependentEntries()
{
var cts = new CancellationTokenSource();
_cache.Set(CacheKeys.DependentCTS, cts);
using (var entry = _cache.CreateEntry(CacheKeys.Parent))
{
// expire this entry if the dependant entry expires.
entry.Value = DateTime.Now;
entry.RegisterPostEvictionCallback(DependentEvictionCallback, this);
_cache.Set(CacheKeys.Child,
DateTime.Now,
new CancellationChangeToken(cts.Token));
}
return RedirectToAction("GetDependentEntries");
}
public IActionResult GetDependentEntries()
{
return View("Dependent", new DependentViewModel
{
ParentCachedTime = _cache.Get<DateTime?>(CacheKeys.Parent),
ChildCachedTime = _cache.Get<DateTime?>(CacheKeys.Child),
Message = _cache.Get<string>(CacheKeys.DependentMessage)
});
}
public IActionResult RemoveChildEntry()
{
_cache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel();
return RedirectToAction("GetDependentEntries");
}
private static void DependentEvictionCallback(object key, object value,
EvictionReason reason, object state)
{
var message = $"Parent entry was evicted. Reason: {reason}.";
((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
}
Durch die Verwendung eines CancellationTokenSource können mehrere Cacheeinträge als Gruppe entfernt werden. Mit dem using
Muster im obigen Code erben Cacheeinträge, die im using
Block erstellt werden, Trigger und Ablaufeinstellungen.
Zusätzliche Hinweise
Der Ablauf erfolgt nicht im Hintergrund. Es gibt keinen Timer, der den Cache aktiv auf abgelaufene Elemente überprüft. Jede Aktivität im Cache (
Get
,Set
,Remove
) kann eine Hintergrundüberprüfung für abgelaufene Elemente auslösen. Ein Timer auf demCancellationTokenSource
(CancelAfter) entfernt auch den Eintrag und löst eine Suche nach abgelaufenen Elementen aus. Im folgenden Beispiel wird CancellationTokenSource(TimeSpan) für das registrierte Token verwendet. Durch Auslösen dieses Tokens wird der Eintrag sofort entfernt, und die Rückrufe für den Entfernungsvorgang werden ausgelöst:public IActionResult CacheAutoExpiringTryGetValueSet() { DateTime cacheEntry; if (!_cache.TryGetValue(CacheKeys.Entry, out cacheEntry)) { cacheEntry = DateTime.Now; var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); var cacheEntryOptions = new MemoryCacheEntryOptions() .AddExpirationToken(new CancellationChangeToken(cts.Token)); _cache.Set(CacheKeys.Entry, cacheEntry, cacheEntryOptions); } return View("Cache", cacheEntry); }
Bei Verwendung eines Rückrufs zum Erneuten Auffüllen eines Cacheelements:
- Mehrere Anfragen können den zwischengespeicherten Schlüsselwert möglicherweise leer vorfinden, da der Callback noch nicht abgeschlossen ist.
- Dies kann dazu führen, dass mehrere Threads das zwischengespeicherte Element erneut auffüllen.
Wenn ein Cacheeintrag zum Erstellen eines anderen verwendet wird, kopiert der untergeordnete Eintrag die Ablauftoken und zeitbasierten Ablaufeinstellungen des übergeordneten Eintrags. Der untergeordnete Eintrag läuft nicht durch manuelles Entfernen oder Aktualisieren des übergeordneten Eintrags ab.
Verwenden Sie PostEvictionCallbacks, um die Rückrufe festzulegen, die ausgelöst werden, nachdem der Eintrag aus dem Cache entfernt wurde. Im Beispielcode wird CancellationTokenSource.Dispose() aufgerufen, um die nicht verwalteten Ressourcen freizugeben, die von
CancellationTokenSource
verwendet werden. DasCancellationTokenSource
wird jedoch nicht sofort entsorgt, da es noch vom Cacheeintrag verwendet wird. DieCancellationToken
wird zuMemoryCacheEntryOptions
übergeben, um einen Cacheeintrag zu erstellen, der nach einer bestimmten Zeit abläuft. Also sollteDispose
nicht aufgerufen werden, bis der Cacheeintrag entfernt oder abgelaufen ist. Der Beispielcode ruft die RegisterPostEvictionCallback-Methode auf, um einen Rückruf zu registrieren, der aufgerufen wird, wenn der Cacheeintrag entfernt wird, und gibtCancellationTokenSource
in diesem Rückruf frei.Für die meisten Apps ist
IMemoryCache
aktiviert. Beispielsweise wirdAddMvc
durch Aufrufen vonAddControllersWithViews
,AddRazorPages
,AddMvcCore().AddRazorViewEngine
,Add{Service}
und vielen anderenConfigureServices
-Methoden inIMemoryCache
aktiviert. Für Apps, die keine der vorherigenAdd{Service}
Methoden aufrufen, kann es erforderlich sein, AddMemoryCache inConfigureServices
aufzurufen.
Aktualisierung des Hintergrundcaches
Verwenden Sie einen Hintergrunddienst wie IHostedService zum Aktualisieren des Cache. Der Hintergrunddienst kann die Einträge neu berechnen und sie dann nur im Cache speichern, wenn sie bereit sind.