ASP.NET Core 内のメモリ内のキャッシュCache in-memory in ASP.NET Core

Rick AndersonJohn luo、および上田 SmithBy Rick Anderson, John Luo, and Steve Smith

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

キャッシュの基本Caching basics

キャッシュを使用すると、コンテンツを生成するために必要な作業量を減らすことで、アプリのパフォーマンスとスケーラビリティを大幅に向上させることができます。Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. キャッシュは、頻繁に変更され 生成に負荷がかかるデータで最適に機能します。Caching works best with data that changes infrequently and is expensive to generate. キャッシュを使用すると、ソースよりもはるかに高速に返されるデータのコピーが作成されます。Caching makes a copy of data that can be returned much faster than from the source. アプリは、キャッシュされたデータに依存し ない ように作成およびテストする必要があります。Apps should be written and tested to never depend on cached data.

ASP.NET Core は、いくつかの異なるキャッシュをサポートしています。ASP.NET Core supports several different caches. 最も単純なキャッシュは、 IMemoryCacheに基づいています。The simplest cache is based on the IMemoryCache. IMemoryCache web サーバーのメモリに格納されているキャッシュを表します。IMemoryCache represents a cache stored in the memory of the web server. サーバーファーム (複数のサーバー) で実行されているアプリでは、メモリ内キャッシュを使用するときにセッションが固定されていることを確認する必要があります。Apps running on a server farm (multiple servers) should ensure sessions are sticky when using the in-memory cache. 固定セッションでは、クライアントからの後続の要求がすべて同じサーバーに送られるようにします。Sticky sessions ensure that subsequent requests from a client all go to the same server. たとえば、Azure Web apps では、 アプリケーション要求ルーティング 処理 (ARR) を使用して、後続のすべての要求を同じサーバーにルーティングします。For example, Azure Web apps use Application Request Routing (ARR) to route all subsequent requests to the same server.

Web ファームの固定されていないセッションでは、キャッシュ整合性の問題を回避するために 分散キャッシュ が必要です。Non-sticky sessions in a web farm require a distributed cache to avoid cache consistency problems. アプリによっては、分散キャッシュがメモリ内キャッシュよりも高いスケールアウトをサポートする場合があります。For some apps, a distributed cache can support higher scale-out than an in-memory cache. 分散キャッシュを使用すると、キャッシュメモリが外部プロセスにオフロードされます。Using a distributed cache offloads the cache memory to an external process.

メモリ内キャッシュには、任意のオブジェクトを格納できます。The in-memory cache can store any object. 分散キャッシュインターフェイスはに制限されてい byte[] ます。The distributed cache interface is limited to byte[]. メモリ内および分散キャッシュストアは、キーと値のペアとしてキャッシュ項目を格納します。The in-memory and distributed cache store cache items as key-value pairs.

System.string. キャッシュ/MemoryCacheSystem.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache (NuGet パッケージ) は、次のものと共に使用できます。System.Runtime.Caching/MemoryCache (NuGet package) can be used with:

  • .NET Standard 2.0 以降。.NET Standard 2.0 or later.
  • .NET Standard 2.0 以降を対象とするすべての .net 実装Any .NET implementation that targets .NET Standard 2.0 or later. たとえば、2.0 以降の ASP.NET Core ます。For example, ASP.NET Core 2.0 or later.
  • .NET Framework 4.5 以降。.NET Framework 4.5 or later.

/ IMemoryCache ASP.NET Core に統合することをお勧めします。 System.Runtime.Caching / そのため、この記事で説明され MemoryCache ているように、このキャッシュはお勧めします。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. たとえば、は IMemoryCache ASP.NET Core 依存関係の挿入とネイティブに連携します。For example, IMemoryCache works natively with ASP.NET Core dependency injection.

System.Runtime.Caching / MemoryCache ASP.NET 4.x から ASP.NET Core にコードを移植するときは、互換性ブリッジとして使用します。Use System.Runtime.Caching/MemoryCache as a compatibility bridge when porting code from ASP.NET 4.x to ASP.NET Core.

キャッシュのガイドラインCache guidelines

  • コードには常に、データをフェッチするためのフォールバックオプションがあり、使用可能なキャッシュ値に依存し ない ようにする必要があります。Code should always have a fallback option to fetch data and not depend on a cached value being available.
  • キャッシュは、不足しているリソース (メモリ) を使用します。The cache uses a scarce resource, memory. キャッシュ拡張の制限:Limit cache growth:

IMemoryCache を使用するUse IMemoryCache

警告

依存関係の挿入から 共有 メモリキャッシュを使用し SetSize 、、 Size 、またはを呼び出して SizeLimit キャッシュサイズを制限すると、アプリが失敗する可能性があります。Using a shared memory cache from Dependency Injection and calling SetSize, Size, or SizeLimit to limit cache size can cause the app to fail. キャッシュにサイズ制限が設定されている場合、すべてのエントリは追加時にサイズを指定する必要があります。When a size limit is set on a cache, all entries must specify a size when being added. これにより、開発者は共有キャッシュを使用する内容を完全に制御できない場合があるため、問題が発生する可能性があります。This can lead to issues since developers may not have full control on what uses the shared cache. たとえば、Entity Framework Core は共有キャッシュを使用し、サイズを指定しません。For example, Entity Framework Core uses the shared cache and does not specify a size. アプリがキャッシュサイズの制限を設定し、EF Core を使用する場合、アプリはをスロー InvalidOperationException します。If an app sets a cache size limit and uses EF Core, the app throws an InvalidOperationException. 、、またはを使用してキャッシュを制限する場合は、キャッシュ SetSize Size SizeLimit 用のキャッシュシングルトンを作成します。When using SetSize, Size, or SizeLimit to limit cache, create a cache singleton for caching. 詳細と例については、「 SetSize、サイズ、および SizeLimit を使用してキャッシュサイズを制限する」を参照してください。For more information and an example, see Use SetSize, Size, and SizeLimit to limit cache size. 共有キャッシュは、他のフレームワークまたはライブラリによって共有されます。A shared cache is one shared by other frameworks or libraries. たとえば、EF Core は共有キャッシュを使用し、サイズを指定しません。For example, EF Core uses the shared cache and does not specify a size.

インメモリキャッシュは、依存関係の挿入を使用してアプリから参照される サービス です。In-memory caching is a service that's referenced from an app using Dependency Injection. IMemoryCacheコンストラクターにインスタンスを要求します。Request the IMemoryCache instance in the constructor:

public class HomeController : Controller
{
    private IMemoryCache _cache;

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

次のコードでは、 TryGetValue を使用して、時間がキャッシュ内にあるかどうかを確認します。The following code uses TryGetValue to check if a time is in the cache. 時間がキャッシュされていない場合は、新しいエントリが作成され、が 設定されたキャッシュに追加されます。If a time isn't cached, a new entry is created and added to the cache with Set. CacheKeysクラスは、ダウンロードサンプルに含まれています。The CacheKeys class is part of the download sample.

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

現在の時刻とキャッシュされた時間が表示されます。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="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>

次のコードでは、 Set extension メソッドを使用して、オブジェクトを作成せずに相対的な時間にデータをキャッシュして MemoryCacheEntryOptions います。The following code uses the Set extension method to cache data for a relative time without creating the MemoryCacheEntryOptions object.

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

キャッシュされた値は、 DateTime タイムアウト期間内に要求があってもキャッシュに残ります。The cached DateTime value remains in the cache while there are requests within the timeout period.

次のコードでは、 GetorcreateGetorcreateasync を使用してデータをキャッシュしています。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> CacheGetOrCreateAsynchronous()
{
    var cacheEntry = await
        _cache.GetOrCreateAsync(CacheKeys.Entry, entry =>
        {
            entry.SlidingExpiration = TimeSpan.FromSeconds(3);
            return Task.FromResult(DateTime.Now);
        });

    return View("Cache", cacheEntry);
}

次のコードは、 Get を呼び出して、キャッシュされた時間を取得します。The following code calls Get to fetch the cached time:

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

次のコードは、絶対有効期限があるキャッシュされた項目を取得または作成します。The following code gets or creates a cached item with absolute expiration:

public IActionResult CacheGetOrCreateAbs()
{
    var cacheEntry = _cache.GetOrCreate(CacheKeys.Entry, entry =>
    {
        entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
        return DateTime.Now;
    });

    return View("Cache", cacheEntry);
}

スライド式有効期限付きのキャッシュされた項目セットは、古くなっている可能性があります。A cached item set with a sliding expiration only is at risk of becoming stale. スライディング有効期間よりも頻繁にアクセスされる場合、項目は期限切れになりません。If it's accessed more frequently than the sliding expiration interval, the item will never expire. スライド式有効期限と絶対有効期限を組み合わせて、その絶対有効期限が過ぎると項目が期限切れになることを保証します。Combine a sliding expiration with an absolute expiration to guarantee that the item expires once its absolute expiration time passes. 絶対有効期限は、項目をキャッシュできる期間を上限として設定し、スライドしている有効期限の間隔内に項目が要求されていない場合は、前に期限切れになることを許可します。The absolute expiration sets an upper bound to how long the item can be cached while still allowing the item to expire earlier if it isn't requested within the sliding expiration interval. 絶対有効期限とスライド式有効期限の両方を指定した場合、有効期限は論理的に論理和になります。When both absolute and sliding expiration are specified, the expirations are logically ORed. スライド式有効期限間隔 または 絶対有効期限が経過すると、項目はキャッシュから削除されます。If either the sliding expiration interval or the absolute expiration time pass, the item is evicted from the cache.

次のコードは、スライディング 絶対の両方の有効期限を持つキャッシュされた項目を取得または作成します。The following code gets or creates a cached item with both sliding and absolute expiration:

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

上記のコードでは、データが絶対時間より長くキャッシュされないことが保証されています。The preceding code guarantees the data will not be cached longer than the absolute time.

GetOrCreateGetOrCreateAsync 、および Get は、クラスの拡張メソッドです CacheExtensionsGetOrCreate, GetOrCreateAsync, and Get are extension methods in the CacheExtensions class. これらのメソッドは、の機能を拡張 IMemoryCache します。These methods extend the capability of IMemoryCache.

MemoryCacheEntryOptionsMemoryCacheEntryOptions

次の例を次に示します。The following sample:

  • スライド式有効期限を設定します。Sets a sliding expiration time. このキャッシュされた項目にアクセスする要求は、スライド式有効期限をリセットします。Requests that access this cached item will reset the sliding expiration clock.
  • キャッシュの優先度を NeverRemoveに設定します。Sets the cache priority to CacheItemPriority.NeverRemove.
  • エントリがキャッシュから削除された後に呼び出される PostEvictionDelegate を設定します。Sets a PostEvictionDelegate that will be called after the entry is evicted from the 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);
}

SetSize、サイズ、および SizeLimit を使用してキャッシュサイズを制限するUse SetSize, Size, and SizeLimit to limit cache size

インスタンスでは、 MemoryCache 必要に応じてサイズ制限を指定して適用できます。A MemoryCache instance may optionally specify and enforce a size limit. キャッシュには、エントリのサイズを測定する機構がないため、キャッシュサイズの制限には定義済みの測定単位がありません。The cache size limit does not have a defined unit of measure because the cache has no mechanism to measure the size of entries. キャッシュサイズの制限が設定されている場合、すべてのエントリでサイズを指定する必要があります。If the cache size limit is set, all entries must specify size. ASP.NET Core ランタイムでは、メモリ負荷に基づいてキャッシュサイズが制限されません。The ASP.NET Core runtime does not limit cache size based on memory pressure. キャッシュサイズを制限するのは開発者だけです。It's up to the developer to limit cache size. 指定されたサイズは、開発者が選択した単位で示されます。The size specified is in units the developer chooses.

次に例を示します。For example:

  • Web アプリが主に文字列をキャッシュしている場合は、各キャッシュエントリのサイズを文字列の長さにすることができます。If the web app was primarily caching strings, each cache entry size could be the string length.
  • アプリでは、すべてのエントリのサイズを1と指定することができ、サイズ制限はエントリの数です。The app could specify the size of all entries as 1, and the size limit is the count of entries.

SizeLimit 設定されていない場合、キャッシュはバインドされずに拡張されます。If SizeLimit isn't set, the cache grows without bound. システムメモリが不足している場合、ASP.NET Core ランタイムはキャッシュをトリミングしません。The ASP.NET Core runtime doesn't trim the cache when system memory is low. アプリは次のように設計する必要があります。Apps must be architected to:

  • キャッシュの拡張を制限します。Limit cache growth.
  • Compact Remove 使用可能なメモリが制限されている場合、またはを呼び出します。Call Compact or Remove when available memory is limited:

次のコードでは、 MemoryCache 依存関係の挿入によってアクセス可能な単位固定サイズが作成されます。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 に単位がありません。SizeLimit does not have units. キャッシュサイズの制限が設定されている場合、キャッシュされたエントリは、最適と思われる任意の単位でサイズを指定する必要があります。Cached entries must specify size in whatever units they deem most appropriate if the cache size limit has been set. キャッシュインスタンスのすべてのユーザーは、同じ単体システムを使用する必要があります。All users of a cache instance should use the same unit system. キャッシュされたエントリサイズの合計がで指定された値を超えた場合、エントリはキャッシュされません SizeLimitAn entry will not be cached if the sum of the cached entry sizes exceeds the value specified by SizeLimit. キャッシュサイズの制限が設定されていない場合、エントリに設定されているキャッシュサイズは無視されます。If no cache size limit is set, the cache size set on the entry will be ignored.

次のコードは、 MyMemoryCache 依存関係挿入 コンテナーに登録します。The following code registers MyMemoryCache with the dependency injection container.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddSingleton<MyMemoryCache>();
}

MyMemoryCache は、このサイズ制限付きキャッシュを認識し、キャッシュエントリサイズを適切に設定する方法を理解しているコンポーネントの独立したメモリキャッシュとして作成されます。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.

次のコードでは、を使用し MyMemoryCache ます。The following code uses 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");
    }
}

キャッシュエントリのサイズは、または拡張メソッドによって設定でき Size SetSize ます。The size of the cache entry can be set by Size or the SetSize extension methods:

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. CompactMemoryCache.Compact

MemoryCache.Compact 指定された割合のキャッシュを次の順序で削除しようとします:MemoryCache.Compact attempts to remove the specified percentage of the cache in the following order:

  • 期限切れのすべての項目。All expired items.
  • 優先順位別の項目。Items by priority. 優先順位の低い項目が最初に削除されます。Lowest priority items are removed first.
  • 最近使用したオブジェクトのうち、最も古いオブジェクト。Least recently used objects.
  • 最も古い絶対有効期限を持つ項目。Items with the earliest absolute expiration.
  • 最も古いスライド式有効期限を持つ項目。Items with the earliest sliding expiration.

優先順位の付いたピン留めされた項目 NeverRemove は削除されません。Pinned items with priority NeverRemove are never removed. 次のコードでは、キャッシュ項目を削除し、を呼び出し Compact ます。The following code removes a cache item and calls Compact:

_cache.Remove(MyKey);

// Remove 33% of cached items.
_cache.Compact(.33);   
cache_size = _cache.Count;

詳細については、 GitHub の「Compact source 」を参照してください。See Compact source on GitHub for more information.

キャッシュの依存関係Cache dependencies

次のサンプルは、依存エントリの有効期限が切れた場合に、キャッシュエントリを期限切れにする方法を示しています。The following sample shows how to expire a cache entry if a dependent entry expires. CancellationChangeTokenがキャッシュされた項目に追加されます。A CancellationChangeToken is added to the cached item. Cancelでが呼び出されると CancellationTokenSource 、両方のキャッシュエントリが削除されます。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);
}

を使用する CancellationTokenSource と、複数のキャッシュエントリを1つのグループとして削除できます。Using a CancellationTokenSource allows multiple cache entries to be evicted as a group. 上記の using コードのパターンでは、ブロック内に作成されたキャッシュエントリによって、 using トリガーと有効期限の設定が継承されます。With the using pattern in the code above, cache entries created inside the using block will inherit triggers and expiration settings.

その他のメモAdditional notes

  • 有効期限はバックグラウンドでは発生しません。Expiration doesn't happen in the background. 期限切れの項目のキャッシュをアクティブにスキャンするタイマーはありません。There is no timer that actively scans the cache for expired items. キャッシュ (、、) のすべてのアクティビティ Get Set は、 Remove 期限切れの項目に対してバックグラウンドスキャンをトリガーできます。Any activity on the cache (Get, Set, Remove) can trigger a background scan for expired items. () のタイマーによって、 CancellationTokenSource CancelAfter エントリも削除され、期限切れの項目のスキャンがトリガーされます。A timer on the CancellationTokenSource (CancelAfter) also removes the entry and triggers a scan for expired items. 次の例では、登録されたトークンに CancellationTokenSource (TimeSpan) を使用します。The following example uses CancellationTokenSource(TimeSpan) for the registered token. このトークンが起動すると、エントリが直ちに削除され、削除コールバックが発生します。When this token fires it removes the entry immediately and fires the eviction callbacks:
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);
}
  • コールバックを使用してキャッシュ項目を再作成する場合:When using a callback to repopulate a cache item:

    • コールバックが完了していないため、複数の要求でキャッシュされたキー値を空にすることができます。Multiple requests can find the cached key value empty because the callback hasn't completed.
    • これにより、複数のスレッドがキャッシュされた項目を再作成する可能性があります。This can result in several threads repopulating the cached item.
  • あるキャッシュエントリを使用して別のキャッシュエントリを作成すると、子は親エントリの有効期限トークンと時間ベースの有効期限の設定をコピーします。When one cache entry is used to create another, the child copies the parent entry's expiration tokens and time-based expiration settings. 親エントリを手動で削除または更新しても、子の有効期限は切れません。The child isn't expired by manual removal or updating of the parent entry.

  • キャッシュ PostEvictionCallbacks からキャッシュエントリが削除された後に起動されるコールバックを設定するには、を使用します。Use PostEvictionCallbacks to set the callbacks that will be fired after the cache entry is evicted from the cache.

  • ほとんどのアプリで IMemoryCache は、が有効になっています。For most apps, IMemoryCache is enabled. たとえば、、、 AddMvc AddControllersWithViews AddRazorPagesAddMvcCore().AddRazorViewEngine 、およびの他の多くのメソッドを呼び出すと、 Add{Service} ConfigureServices が有効になり IMemoryCache ます。For example, calling AddMvc, AddControllersWithViews, AddRazorPages, AddMvcCore().AddRazorViewEngine, and many other Add{Service} methods in ConfigureServices, enables IMemoryCache. 上記のメソッドのいずれかを呼び出さないアプリでは Add{Service} 、でを呼び出す必要がある場合があり AddMemoryCache ConfigureServices ます。For apps that are not calling one of the preceding Add{Service} methods, it may be necessary to call AddMemoryCache in ConfigureServices.

バックグラウンドキャッシュ更新Background cache update

などの バックグラウンドサービス を使用して IHostedService キャッシュを更新します。Use a background service such as IHostedService to update the cache. バックグラウンドサービスでは、エントリを再計算して、準備ができたときにのみキャッシュに割り当てることができます。The background service can recompute the entries and then assign them to the cache only when they’re ready.

その他のリソースAdditional resources

Rick AndersonJohn luo、および上田 SmithBy Rick Anderson, John Luo, and Steve Smith

サンプル コードを表示またはダウンロードします (ダウンロード方法)。View or download sample code (how to download)

キャッシュの基本Caching basics

キャッシュを使用すると、コンテンツを生成するために必要な作業量を減らすことで、アプリのパフォーマンスとスケーラビリティを大幅に向上させることができます。Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. キャッシュは、あまり変更されないデータで最適に機能します。Caching works best with data that changes infrequently. キャッシュを使用すると、元のソースよりもはるかに高速に返されるデータのコピーが作成されます。Caching makes a copy of data that can be returned much faster than from the original source. コードは、キャッシュされたデータに依存し ない ように記述してテストする必要があります。Code should be written and tested to never depend on cached data.

ASP.NET Core は、いくつかの異なるキャッシュをサポートしています。ASP.NET Core supports several different caches. 最も単純なキャッシュは、web サーバーのメモリに格納されているキャッシュを表す IMemoryCacheに基づいています。The simplest cache is based on the IMemoryCache, which represents a cache stored in the memory of the web server. サーバーファーム (複数のサーバー) で実行されるアプリでは、メモリ内キャッシュを使用するときにセッションが固定されていることを確認する必要があります。Apps that run on a server farm (multiple servers) should ensure that sessions are sticky when using the in-memory cache. 固定セッションでは、クライアントからの以降の要求がすべて同じサーバーに送られるようにします。Sticky sessions ensure that later requests from a client all go to the same server. たとえば、Azure Web apps は、 アプリケーション要求ルーティング 処理 (ARR) を使用して、ユーザーエージェントからのすべての要求を同じサーバーにルーティングします。For example, Azure Web apps use Application Request Routing (ARR) to route all requests from a user agent to the same server.

Web ファームの固定されていないセッションでは、キャッシュ整合性の問題を回避するために 分散キャッシュ が必要です。Non-sticky sessions in a web farm require a distributed cache to avoid cache consistency problems. アプリによっては、分散キャッシュがメモリ内キャッシュよりも高いスケールアウトをサポートする場合があります。For some apps, a distributed cache can support higher scale-out than an in-memory cache. 分散キャッシュを使用すると、キャッシュメモリが外部プロセスにオフロードされます。Using a distributed cache offloads the cache memory to an external process.

メモリ内キャッシュには、任意のオブジェクトを格納できます。The in-memory cache can store any object. 分散キャッシュインターフェイスはに制限されてい byte[] ます。The distributed cache interface is limited to byte[]. メモリ内および分散キャッシュストアは、キーと値のペアとしてキャッシュ項目を格納します。The in-memory and distributed cache store cache items as key-value pairs.

System.string. キャッシュ/MemoryCacheSystem.Runtime.Caching/MemoryCache

System.Runtime.Caching/MemoryCache (NuGet パッケージ) は、次のものと共に使用できます。System.Runtime.Caching/MemoryCache (NuGet package) can be used with:

  • .NET Standard 2.0 以降。.NET Standard 2.0 or later.
  • .NET Standard 2.0 以降を対象とするすべての .net 実装Any .NET implementation that targets .NET Standard 2.0 or later. たとえば、2.0 以降の ASP.NET Core ます。For example, ASP.NET Core 2.0 or later.
  • .NET Framework 4.5 以降。.NET Framework 4.5 or later.

/ IMemoryCache ASP.NET Core に統合することをお勧めします。 System.Runtime.Caching / そのため、この記事で説明され MemoryCache ているように、このキャッシュはお勧めします。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. たとえば、は IMemoryCache ASP.NET Core 依存関係の挿入とネイティブに連携します。For example, IMemoryCache works natively with ASP.NET Core dependency injection.

System.Runtime.Caching / MemoryCache ASP.NET 4.x から ASP.NET Core にコードを移植するときは、互換性ブリッジとして使用します。Use System.Runtime.Caching/MemoryCache as a compatibility bridge when porting code from ASP.NET 4.x to ASP.NET Core.

キャッシュのガイドラインCache guidelines

  • コードには常に、データをフェッチするためのフォールバックオプションがあり、使用可能なキャッシュ値に依存し ない ようにする必要があります。Code should always have a fallback option to fetch data and not depend on a cached value being available.
  • キャッシュは、不足しているリソース (メモリ) を使用します。The cache uses a scarce resource, memory. キャッシュ拡張の制限:Limit cache growth:

IMemoryCache の使用Using IMemoryCache

警告

依存関係の挿入から 共有 メモリキャッシュを使用し SetSize 、、 Size 、またはを呼び出して SizeLimit キャッシュサイズを制限すると、アプリが失敗する可能性があります。Using a shared memory cache from Dependency Injection and calling SetSize, Size, or SizeLimit to limit cache size can cause the app to fail. キャッシュにサイズ制限が設定されている場合、すべてのエントリは追加時にサイズを指定する必要があります。When a size limit is set on a cache, all entries must specify a size when being added. これにより、開発者は共有キャッシュを使用する内容を完全に制御できない場合があるため、問題が発生する可能性があります。This can lead to issues since developers may not have full control on what uses the shared cache. たとえば、Entity Framework Core は共有キャッシュを使用し、サイズを指定しません。For example, Entity Framework Core uses the shared cache and does not specify a size. アプリがキャッシュサイズの制限を設定し、EF Core を使用する場合、アプリはをスロー InvalidOperationException します。If an app sets a cache size limit and uses EF Core, the app throws an InvalidOperationException. 、、またはを使用してキャッシュを制限する場合は、キャッシュ SetSize Size SizeLimit 用のキャッシュシングルトンを作成します。When using SetSize, Size, or SizeLimit to limit cache, create a cache singleton for caching. 詳細と例については、「 SetSize、サイズ、および SizeLimit を使用してキャッシュサイズを制限する」を参照してください。For more information and an example, see Use SetSize, Size, and SizeLimit to limit cache size.

インメモリキャッシュは、依存関係の挿入を使用してアプリから参照される サービス です。In-memory caching is a service that's referenced from your app using Dependency Injection. AddMemoryCacheでの呼び出し 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();
    }
}

IMemoryCacheコンストラクターにインスタンスを要求します。Request the IMemoryCache instance in the constructor:

public class HomeController : Controller
{
    private IMemoryCache _cache;

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

IMemoryCacheでは、 AspNetCore メタパッケージで入手できる NuGet パッケージmicrosoft. extension. Memoryが必要です。IMemoryCache requires NuGet package Microsoft.Extensions.Caching.Memory, which is available in the Microsoft.AspNetCore.App metapackage.

次のコードでは、 TryGetValue を使用して、時間がキャッシュ内にあるかどうかを確認します。The following code uses TryGetValue to check if a time is in the cache. 時間がキャッシュされていない場合は、新しいエントリが作成され、が 設定されたキャッシュに追加されます。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);
}

現在の時刻とキャッシュされた時間が表示されます。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>

キャッシュされた値は、 DateTime タイムアウト期間内に要求があってもキャッシュに残ります。The cached DateTime value remains in the cache while there are requests within the timeout period. 次の図は、現在の時刻と、キャッシュから取得された古い時間を示しています。The following image shows the current time and an older time retrieved from the cache:

2つの異なる時刻が表示されたインデックスビュー

次のコードでは、 GetorcreateGetorcreateasync を使用してデータをキャッシュしています。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);
}

次のコードは、 Get を呼び出して、キャッシュされた時間を取得します。The following code calls Get to fetch the cached time:

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

GetOrCreateGetOrCreateAsync 、および Get は、の機能を拡張する cacheextensions クラスの拡張メソッドです IMemoryCacheGetOrCreate , GetOrCreateAsync, and Get are extension methods part of the CacheExtensions class that extends the capability of IMemoryCache. 他のキャッシュメソッドの詳細については、「 IMemoryCache メソッドcacheextensions メソッド 」を参照してください。See IMemoryCache methods and CacheExtensions methods for a description of other cache methods.

MemoryCacheEntryOptionsMemoryCacheEntryOptions

次の例を次に示します。The following sample:

  • スライド式有効期限を設定します。Sets a sliding expiration time. このキャッシュされた項目にアクセスする要求は、スライド式有効期限をリセットします。Requests that access this cached item will reset the sliding expiration clock.
  • キャッシュの優先順位をに設定し CacheItemPriority.NeverRemove ます。Sets the cache priority to CacheItemPriority.NeverRemove.
  • エントリがキャッシュから削除された後に呼び出される PostEvictionDelegate を設定します。Sets a PostEvictionDelegate that will be called after the entry is evicted from the 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);
}

SetSize、サイズ、および SizeLimit を使用してキャッシュサイズを制限するUse SetSize, Size, and SizeLimit to limit cache size

インスタンスでは、 MemoryCache 必要に応じてサイズ制限を指定して適用できます。A MemoryCache instance may optionally specify and enforce a size limit. キャッシュには、エントリのサイズを測定する機構がないため、キャッシュサイズの制限には定義済みの測定単位がありません。The cache size limit does not have a defined unit of measure because the cache has no mechanism to measure the size of entries. キャッシュサイズの制限が設定されている場合、すべてのエントリでサイズを指定する必要があります。If the cache size limit is set, all entries must specify size. ASP.NET Core ランタイムでは、メモリ負荷に基づいてキャッシュサイズが制限されません。The ASP.NET Core runtime does not limit cache size based on memory pressure. キャッシュサイズを制限するのは開発者だけです。It's up to the developer to limit cache size. 指定されたサイズは、開発者が選択した単位で示されます。The size specified is in units the developer chooses.

次に例を示します。For example:

  • Web アプリが主に文字列をキャッシュしている場合は、各キャッシュエントリのサイズを文字列の長さにすることができます。If the web app was primarily caching strings, each cache entry size could be the string length.
  • アプリでは、すべてのエントリのサイズを1と指定することができ、サイズ制限はエントリの数です。The app could specify the size of all entries as 1, and the size limit is the count of entries.

SizeLimitが設定されていない場合、キャッシュはバインドされずに拡張されます。If SizeLimit is not set, the cache grows without bound. ASP.NET Core ランタイムは、システムメモリが不足しているとキャッシュをトリミングしません。The ASP.NET Core runtime does not trim the cache when system memory is low. アプリは次のように設計されています。Apps much be architected to:

  • キャッシュの拡張を制限します。Limit cache growth.
  • Compact Remove 使用可能なメモリが制限されている場合、またはを呼び出します。Call Compact or Remove when available memory is limited:

次のコードでは、 MemoryCache 依存関係の挿入によってアクセス可能な単位固定サイズが作成されます。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 に単位がありません。SizeLimit does not have units. キャッシュサイズの制限が設定されている場合、キャッシュされたエントリは、最適と思われる任意の単位でサイズを指定する必要があります。Cached entries must specify size in whatever units they deem most appropriate if the cache size limit has been set. キャッシュインスタンスのすべてのユーザーは、同じ単体システムを使用する必要があります。All users of a cache instance should use the same unit system. キャッシュされたエントリサイズの合計がで指定された値を超えた場合、エントリはキャッシュされません SizeLimitAn entry will not be cached if the sum of the cached entry sizes exceeds the value specified by SizeLimit. キャッシュサイズの制限が設定されていない場合、エントリに設定されているキャッシュサイズは無視されます。If no cache size limit is set, the cache size set on the entry will be ignored.

次のコードは、 MyMemoryCache 依存関係挿入 コンテナーに登録します。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 は、このサイズ制限付きキャッシュを認識し、キャッシュエントリサイズを適切に設定する方法を理解しているコンポーネントの独立したメモリキャッシュとして作成されます。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.

次のコードでは、を使用し 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");
    }
}

キャッシュエントリのサイズは、 size または SetSize 拡張メソッドを使用して設定できます。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");
}

MemoryCache. CompactMemoryCache.Compact

MemoryCache.Compact 指定された割合のキャッシュを次の順序で削除しようとします:MemoryCache.Compact attempts to remove the specified percentage of the cache in the following order:

  • 期限切れのすべての項目。All expired items.
  • 優先順位別の項目。Items by priority. 優先順位の低い項目が最初に削除されます。Lowest priority items are removed first.
  • 最近使用したオブジェクトのうち、最も古いオブジェクト。Least recently used objects.
  • 最も古い絶対有効期限を持つ項目。Items with the earliest absolute expiration.
  • 最も古いスライド式有効期限を持つ項目。Items with the earliest sliding expiration.

優先順位の付いたピン留めされた項目 NeverRemove は削除されません。Pinned items with priority NeverRemove are never removed.

_cache.Remove(MyKey);

// Remove 33% of cached items.
_cache.Compact(.33);   
cache_size = _cache.Count;

詳細については、 GitHub の「Compact source 」を参照してください。See Compact source on GitHub for more information.

キャッシュの依存関係Cache dependencies

次のサンプルは、依存エントリの有効期限が切れた場合に、キャッシュエントリを期限切れにする方法を示しています。The following sample shows how to expire a cache entry if a dependent entry expires. CancellationChangeTokenがキャッシュされた項目に追加されます。A CancellationChangeToken is added to the cached item. Cancelでが呼び出されると CancellationTokenSource 、両方のキャッシュエントリが削除されます。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);
}

を使用する CancellationTokenSource と、複数のキャッシュエントリを1つのグループとして削除できます。Using a CancellationTokenSource allows multiple cache entries to be evicted as a group. 上記の using コードのパターンでは、ブロック内に作成されたキャッシュエントリによって、 using トリガーと有効期限の設定が継承されます。With the using pattern in the code above, cache entries created inside the using block will inherit triggers and expiration settings.

その他のメモAdditional notes

  • コールバックを使用してキャッシュ項目を再作成する場合:When using a callback to repopulate a cache item:

    • コールバックが完了していないため、複数の要求でキャッシュされたキー値を空にすることができます。Multiple requests can find the cached key value empty because the callback hasn't completed.
    • これにより、複数のスレッドがキャッシュされた項目を再作成する可能性があります。This can result in several threads repopulating the cached item.
  • あるキャッシュエントリを使用して別のキャッシュエントリを作成すると、子は親エントリの有効期限トークンと時間ベースの有効期限の設定をコピーします。When one cache entry is used to create another, the child copies the parent entry's expiration tokens and time-based expiration settings. 親エントリを手動で削除または更新しても、子の有効期限は切れません。The child isn't expired by manual removal or updating of the parent entry.

  • キャッシュからキャッシュエントリが削除された後に起動されるコールバックを設定するには、 PostEvictionCallbacks を使用します。Use PostEvictionCallbacks to set the callbacks that will be fired after the cache entry is evicted from the cache.

バックグラウンドキャッシュ更新Background cache update

などの バックグラウンドサービス を使用して IHostedService キャッシュを更新します。Use a background service such as IHostedService to update the cache. バックグラウンドサービスでは、エントリを再計算して、準備ができたときにのみキャッシュに割り当てることができます。The background service can recompute the entries and then assign them to the cache only when they’re ready.

その他のリソースAdditional resources