Memorizzare nella cache in memoria in ASP.NET CoreCache in-memory in ASP.NET Core

Dal Rick Anderson, John Luo, e Steve SmithBy Rick Anderson, John Luo, and Steve Smith

Visualizzare o scaricare il codice di esempio (procedura per il download)View or download sample code (how to download)

Nozioni di base di memorizzazione nella cacheCaching basics

La memorizzazione nella cache può migliorare significativamente le prestazioni e scalabilità di un'app, riducendo il lavoro necessario per generare il contenuto.Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. La memorizzazione nella cache funziona meglio con i dati che vengono modificati raramente.Caching works best with data that changes infrequently. La memorizzazione nella cache esegue una copia di dati che possono essere restituiti molto più veloce dall'origine dati originale.Caching makes a copy of data that can be returned much faster than from the original source. Le app devono essere scritte e testate per mai dipendono dai dati memorizzati nella cache.Apps should be written and tested to never depend on cached data.

ASP.NET Core supporta diverse cache diverse.ASP.NET Core supports several different caches. La cache più semplice si basa sulla IMemoryCache, che rappresenta una cache archiviata nella memoria del server web.The simplest cache is based on the IMemoryCache, which represents a cache stored in the memory of the web server. Le app eseguite in una server farm di più server è necessario assicurarsi che le sessioni sono permanenti quando si usa la cache in memoria.Apps that run on a server farm of multiple servers should ensure that sessions are sticky when using the in-memory cache. Sessioni permanenti assicurarsi che le successive richieste da un client tutte andare allo stesso server.Sticky sessions ensure that subsequent requests from a client all go to the same server. Ad esempio, uso di App Web di Azure Application Request Routing (ARR) per indirizzare tutte le richieste successive nello stesso server.For example, Azure Web apps use Application Request Routing (ARR) to route all subsequent requests to the same server.

Richiedono sessioni permanenti con non in una web farm un' cache distribuita per evitare problemi di coerenza della cache.Non-sticky sessions in a web farm require a distributed cache to avoid cache consistency problems. Per alcune App, una cache distribuita può supportare l'aumento maggiore rispetto a un'istanza di cache in memoria.For some apps, a distributed cache can support higher scale-out than an in-memory cache. Usando una cache distribuita ripartisce la memoria cache a un processo esterno.Using a distributed cache offloads the cache memory to an external process.

Il IMemoryCache cache rimuoverà le voci della cache eccessivo della memoria, a meno che il memorizza nella cache priorità è impostata su CacheItemPriority.NeverRemove.The IMemoryCache cache will evict cache entries under memory pressure unless the cache priority is set to CacheItemPriority.NeverRemove. È possibile impostare il CacheItemPriority per modificare la priorità con cui la cache rimuove gli elementi sotto pressione della memoria.You can set the CacheItemPriority to adjust the priority with which the cache evicts items under memory pressure.

La cache in memoria può archiviare qualsiasi oggetto. l'interfaccia di cache distribuita è limitata a byte[].The in-memory cache can store any object; the distributed cache interface is limited to byte[]. Gli elementi di cache di archivio della cache in memoria e distribuita come coppie chiave-valore.The in-memory and distributed cache store cache items as key-value pairs.

System.Runtime.Caching/MemoryCacheSystem.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache (Mobileengagement) può essere usato con:System.Runtime.Caching/MemoryCache (NuGet package) can be used with:

  • .NET standard 2.0 o versione successiva..NET Standard 2.0 or later.
  • Eventuali implementazione di .NET che ha come destinazione .NET Standard 2.0 o versione successiva.Any .NET implementation that targets .NET Standard 2.0 or later. Ad esempio, ASP.NET Core 2.0 o versione successiva.For example, ASP.NET Core 2.0 or later.
  • .NET framework 4.5 o versione successiva..NET Framework 4.5 or later.

Extensions / IMemoryCache (descritto in questo articolo) è consigliato rispetto System.Runtime.Caching / MemoryCache perché è meglio integrato in ASP.NET Core.Microsoft.Extensions.Caching.Memory/IMemoryCache (described in this article) is recommended over System.Runtime.Caching/MemoryCache because it's better integrated into ASP.NET Core. Ad esempio, IMemoryCache funziona in modo nativo con ASP.NET Core inserimento delle dipendenze.For example, IMemoryCache works natively with ASP.NET Core dependency injection.

Uso System.Runtime.Caching / MemoryCache come un bridge di compatibilità durante il porting del codice da ASP.NET 4.x ad ASP.NET Core.Use System.Runtime.Caching/MemoryCache as a compatibility bridge when porting code from ASP.NET 4.x to ASP.NET Core.

Linee guida per la cacheCache guidelines

  • Codice deve essere associato sempre un'opzione di fallback per recuperare i dati e non dipendono dal valore memorizzato nella cache siano disponibili.Code should always have a fallback option to fetch data and not depend on a cached value being available.
  • La cache utilizza una risorsa limitata, della memoria.The cache uses a scarce resource, memory. Limitare l'aumento delle dimensioni della cache:Limit cache growth:

Usando IMemoryCacheUsing IMemoryCache

Memorizzazione nella cache in memoria è una assistenza cui viene fatto riferimento dalla tua app usando inserimento delle dipendenze.In-memory caching is a service that's referenced from your app using Dependency Injection. Chiamare AddMemoryCache in ConfigureServices:Call AddMemoryCache in ConfigureServices:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMemoryCache();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvcWithDefaultRoute();
    }
}

Richiedere il IMemoryCache istanza nel costruttore:Request the IMemoryCache instance in the constructor:

public class HomeController : Controller
{
    private IMemoryCache _cache;

    public HomeController(IMemoryCache memoryCache)
    {
        _cache = memoryCache;
    }

IMemoryCache richiede il pacchetto NuGet Extensions.IMemoryCache requires NuGet package Microsoft.Extensions.Caching.Memory.

IMemoryCache richiede il pacchetto NuGet Extensions, che è disponibile nel metapacchetto Microsoft. aspnetcore.IMemoryCache requires NuGet package Microsoft.Extensions.Caching.Memory, which is available in the Microsoft.AspNetCore.All metapackage.

IMemoryCache richiede il pacchetto NuGet Extensions, che è disponibile nel Microsoft.AspNetCore.App metapacchetto.IMemoryCache requires NuGet package Microsoft.Extensions.Caching.Memory, which is available in the Microsoft.AspNetCore.App metapackage.

Il codice seguente usa TryGetValue per verificare se è presente una volta nella cache.The following code uses TryGetValue to check if a time is in the cache. Se non viene memorizzato nella cache una volta, una nuova voce viene creata e aggiunto alla cache con impostare.If a time isn't cached, a new entry is created and added to the cache with Set.

public static class CacheKeys
{
    public static string Entry { get { return "_Entry"; } }
    public static string CallbackEntry { get { return "_Callback"; } }
    public static string CallbackMessage { get { return "_CallbackMessage"; } }
    public static string Parent { get { return "_Parent"; } }
    public static string Child { get { return "_Child"; } }
    public static string DependentMessage { get { return "_DependentMessage"; } }
    public static string DependentCTS { get { return "_DependentCTS"; } }
    public static string Ticks { get { return "_Ticks"; } }
    public static string CancelMsg { get { return "_CancelMsg"; } }
    public static string CancelTokenSource { get { return "_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);
}

Vengono visualizzati l'ora corrente e l'ora memorizzati nella cache:The current time and the cached time are displayed:

@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="CacheGetOrCreateAsync">GetOrCreateAsync</a></li>
        <li><a asp-controller="Home" asp-action="CacheRemove">Remove</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>

Memorizzato nella cache DateTime valore rimane nella cache anche se esistono richieste nel periodo di timeout.The cached DateTime value remains in the cache while there are requests within the timeout period. L'immagine seguente mostra l'ora corrente e un'ora precedente recuperato dalla cache:The following image shows the current time and an older time retrieved from the cache:

Vista Index con due diverse volte visualizzato

Il codice seguente usa GetOrCreate e GetOrCreateAsync per memorizzare i dati.The following code uses GetOrCreate and GetOrCreateAsync to cache data.

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> CacheGetOrCreateAsync()
{
    var cacheEntry = await
        _cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
    {
        entry.SlidingExpiration = TimeSpan.FromSeconds(3);
        return Task.FromResult(DateTime.Now);
    });

    return View("Cache", cacheEntry);
}

Il codice seguente chiama ottenere per recuperare l'ora memorizzati nella cache:The following code calls Get to fetch the cached time:

public IActionResult CacheGet()
{
    var cacheEntry = _cache.Get<DateTime?>(CacheKeys.Entry);
    return View("Cache", cacheEntry);
}

GetOrCreate , GetOrCreateAsync, e ottenere fanno parte di metodi di estensione del CacheExtensions classe che estende le funzionalità di IMemoryCache.GetOrCreate , GetOrCreateAsync, and Get are extension methods part of the CacheExtensions class that extends the capability of IMemoryCache. Visualizzare metodi IMemoryCache e CacheExtensions metodi per una descrizione degli altri metodi della cache.See IMemoryCache methods and CacheExtensions methods for a description of other cache methods.

MemoryCacheEntryOptionsMemoryCacheEntryOptions

L'esempio seguente:The following sample:

  • Imposta l'ora di scadenza assoluta.Sets the absolute expiration time. Questo è il tempo massimo che può essere memorizzato nella cache la voce e impedisce la voce diventi obsoleta quando la scadenza variabile in modo continuo viene rinnovata.This is the maximum time the entry can be cached and prevents the item from becoming too stale when the sliding expiration is continuously renewed.
  • Imposta una scadenza variabile.Sets a sliding expiration time. Le richieste che accedono a questo elemento memorizzato nella cache verranno reimpostato il clock di scadenza scorrevole.Requests that access this cached item will reset the sliding expiration clock.
  • Imposta la priorità della cache su CacheItemPriority.NeverRemove.Sets the cache priority to CacheItemPriority.NeverRemove.
  • Imposta una PostEvictionDelegate che verrà chiamato dopo la voce viene rimosso dalla cache.Sets a PostEvictionDelegate that will be called after the entry is evicted from the cache. Il callback viene eseguito su un thread diverso dal codice che rimuove l'elemento dalla cache.The callback is run on a different thread from the code that removes the item from the cache.
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);
}

Usare SetSize, dimensioni e SizeLimit per limitare le dimensioni della cacheUse SetSize, Size, and SizeLimit to limit cache size

Oggetto MemoryCache istanza può facoltativamente specificare e applicare un limite di dimensione.A MemoryCache instance may optionally specify and enforce a size limit. Il limite delle dimensioni di memoria non è un'unità di misura definita perché la cache è disponibile alcun meccanismo per misurare le dimensioni delle voci.The memory size limit does not have a defined unit of measure because the cache has no mechanism to measure the size of entries. Se la dimensione massima di memoria cache è impostata, tutte le voci devono specificare dimensioni.If the cache memory size limit is set, all entries must specify size. Il runtime di ASP.NET Core non limita le dimensioni di cache basata su richieste di memoria.The ASP.NET Core runtime does not limit cache size based on memory pressure. Spetta allo sviluppatore per limitare le dimensioni della cache.It's up to the developer to limit cache size. La dimensione specificata è lo sviluppatore decide di unità.The size specified is in units the developer chooses.

Ad esempio:For example:

  • Se l'app web è stata principalmente la memorizzazione nella cache le stringhe, ciascuna dimensione della voce della cache potrebbe essere la lunghezza della stringa.If the web app was primarily caching strings, each cache entry size could be the string length.
  • L'app è stato possibile specificare la dimensione di tutte le voci come 1 e la dimensione massima è il numero di voci.The app could specify the size of all entries as 1, and the size limit is the count of entries.

Il codice seguente crea un valore a dimensione fissa MemoryCache accessibile dal inserimento delle dipendenze:The following code creates a unitless fixed size MemoryCache accessible by dependency injection:

// using Microsoft.Extensions.Caching.Memory;
public class MyMemoryCache 
{
    public MemoryCache Cache { get; set; }
    public MyMemoryCache()
    {
        Cache = new MemoryCache(new MemoryCacheOptions
        {
            SizeLimit = 1024
        });
    }
}

SizeLimit non dispone di unità.SizeLimit does not have units. Le voci memorizzate nella cache è necessario specificare le dimensioni in qualsiasi unità che ritengono più appropriati se le dimensioni della memoria cache sono stata impostata.Cached entries must specify size in whatever units they deem most appropriate if the cache memory size has been set. Tutti gli utenti di un'istanza di cache devono usare lo stesso sistema di unità.All users of a cache instance should use the same unit system. Una voce non verrà memorizzata se la somma delle dimensioni della voce memorizzata nella cache supera il valore specificato da SizeLimit.An entry will not be cached if the sum of the cached entry sizes exceeds the value specified by SizeLimit. Se non è impostato alcun limite di dimensione della cache, le dimensioni della cache impostato sulla voce verranno ignorata.If no cache size limit is set, the cache size set on the entry will be ignored.

Nell'esempio di codice registri MyMemoryCache con il inserimento delle dipendenze contenitore.The following code registers MyMemoryCache with the dependency injection container.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddSingleton<MyMemoryCache>();
}

MyMemoryCache viene creata come una cache di memoria indipendenti per i componenti che sono a conoscenza di questa cache di dimensioni limitate e sapere come impostare le dimensioni di voce della cache in modo appropriato.MyMemoryCache is created as an independent memory cache for components that are aware of this size limited cache and know how to set cache entry size appropriately.

Il codice seguente usa MyMemoryCache:The following code uses MyMemoryCache:

public class AboutModel : PageModel
{
    private MemoryCache _cache;
    public static readonly string MyKey = "_MyKey";

    public AboutModel(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");
    }
}

Le dimensioni della voce della cache possono essere impostate tramite dimensioni o nella SetSize metodo di estensione:The size of the cache entry can be set by Size or the SetSize extension method:

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

Dipendenze della cacheCache dependencies

L'esempio seguente viene illustrato come la scadenza di una voce della cache se una voce di dipendente scade.The following sample shows how to expire a cache entry if a dependent entry expires. Oggetto CancellationChangeToken viene aggiunto all'elemento memorizzato nella cache.A CancellationChangeToken is added to the cached item. Quando Cancel viene chiamato sul CancellationTokenSource, entrambe le voci della cache vengono rimossi.When Cancel is called on the CancellationTokenSource, both cache entries are evicted.

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

Usando un CancellationTokenSource consente a più voci della cache essere eliminata come un gruppo.Using a CancellationTokenSource allows multiple cache entries to be evicted as a group. Con il using motivo nel codice precedente, le voci della cache creata all'interno di using blocco erediterà i trigger e le impostazioni di scadenza.With the using pattern in the code above, cache entries created inside the using block will inherit triggers and expiration settings.

Note aggiuntiveAdditional notes

  • Quando si usa un callback per ripopolare un elemento della cache:When using a callback to repopulate a cache item:

    • Più richieste possono trovare vuoto il valore della chiave memorizzata nella cache perché non è stato completato il callback.Multiple requests can find the cached key value empty because the callback hasn't completed.
    • Ciò può comportare diversi thread ripopolamento l'elemento memorizzato nella cache.This can result in several threads repopulating the cached item.
  • Quando una voce della cache viene usata per creare un altro, l'elemento figlio copia token scadenza della voce padre e le impostazioni di scadenza basati sul tempo.When one cache entry is used to create another, the child copies the parent entry's expiration tokens and time-based expiration settings. L'elemento figlio non è scaduto per la rimozione manuale o l'aggiornamento della voce padre.The child isn't expired by manual removal or updating of the parent entry.

  • Uso PostEvictionCallbacks per impostare i callback che verranno generati dopo che la voce della cache viene rimosso dalla cache.Use PostEvictionCallbacks to set the callbacks that will be fired after the cache entry is evicted from the cache.

Risorse aggiuntiveAdditional resources