Skip to content

投稿

ASP.Net Coreでリクエストキャッシングを実装する

2019年7月8日 • 5 分で読める

ASP.Net Coreでリクエストキャッシングを実装する

アプリケーション開発のある時点で、通常かなり早い段階で、アプリケーションが遅いことに気付きます。調査の結果、原因は不必要に同じデータを繰り返し取得していることであり、ひらめきが訪れ、こう思います:「キャッシングが必要だ。」

キャッシングは、データベースまたはサードパーティAPIへの冗長な呼び出しを排除するための非常に価値のあるパターンです。Microsoftは時間ベースのキャッシング用にIMemoryCacheを提供していますが、時間ベースのキャッシングが必要でない場合もあります。この記事では、リクエストスコープキャッシングとそのメリットについて説明します。

リクエストキャッシングとは何ですか?リクエストキャッシングは、Webリクエストの期間中データをキャッシュするメカニズムです。dot-netでは、HttpContext.Itemsコレクションでこの機能をある程度持っていますが、HttpContextは注入可能性で知られていません。

リクエストスコープキャッシングにはいくつかのメリットがあります。まず、古いデータの懸念を排除します。ほとんどのシナリオでは、リクエストは1秒未満で実行され、通常はデータが古くなるのに十分な時間ではありません。次に、リクエストが終了するとデータが消滅するため、有効期限は問題になりません。

Asp.Net Coreは、すぐに使用できる注入可能なキャッシングを持っていません。前述のように、HttpContext.Itemsはオプションですが、エレガントなソリューションではありません。

幸いなことに、ASP.Net Coreは組み込みの依存性注入(DI)フレームワークを使用して、注入可能なリクエストキャッシング実装を作成するためのツールを提供しています。

組み込みのDIフレームワークには、依存性の3つのライフタイムがあります:SingletonScoped、およびTransientSingletonはアプリケーションの期間、Scopedはリクエストの期間、Transientは各リクエストでの新しいインスタンスです。

一貫性を保つために、IMemoryCacheインターフェースをモデルにしたインターフェースを作成しました。

インターフェース

public interface IRequestCache
{
    /// <summary>
    /// Add the value into request cache. If the key already exists, the value is overwritten.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="TValue"></typeparam>
    void Add<TValue>(string key, TValue value);

    /// <summary>
    /// Remove the key from the request cache
    /// </summary>
    /// <param name="key"></param>
    void Remove(string key);

    /// <summary>
    /// Retrieve the value by key, if the key is not in the cache then the add func is called
    /// adding the value to cache and returning the added value.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="add"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    TValue RetrieveOrAdd<TValue>(string key, Func<TValue> add);

    /// <summary>
    /// Retrieves the value by key. When the key does not exist the default value for the type is returned.
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    TValue Retrieve<TValue>(string key);
}

実装

public class RequestCache : IRequestCache
{
    IDictionary<string, object> _cache = new Dictionary<string, object>();

    /// <summary>
    /// Add the value into request cache. If the key already exists, the value is overwritten.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <typeparam name="TValue"></typeparam>
    public void Add<TValue>(string key, TValue value)
    {
        _cache[key] = value;
    }

    /// <summary>
    /// Remove the key from the request cache
    /// </summary>
    /// <param name="key"></param>
    public void Remove(string key)
    {
        if (_cache.ContainsKey(key))
        {
            _cache.Remove(key);
        }
    }

    /// <summary>
    /// Retrieve the value by key, if the key is not in the cache then the add func is called
    /// adding the value to cache and returning the added value.
    /// </summary>
    /// <param name="key"></param>
    /// <param name="add"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    public TValue RetrieveOrAdd<TValue>(string key, Func<TValue> add)
    {
        if (_cache.ContainsKey(key))
        {
            return (TValue)_cache[key];
        }

        var value = add();

        _cache[key] = value;

        return value;
    }

    /// <summary>
    /// Retrieves the value by key. When the key does not exist the default value for the type is returned.
    /// </summary>
    /// <param name="key"></param>
    /// <typeparam name="TValue"></typeparam>
    /// <returns></returns>
    public TValue Retrieve<TValue>(string key)
    {
        if (_cache.ContainsKey(key))
        {
            return (TValue)_cache[key];
        }

        return default(TValue);
    }
}

ASP.Net CoreのDIフレームワークを使用して、Scopedとして配線します。

services.AddScoped<IRequestCache, RequestCache>();

使用方法

public class UserService
{
    private readonly IRequestCache _cache;
    private readonly IUserRepository _userRepository;

    public UserService(IRequestCache cache, IUserRepository userRepository)
    {
        _cache = cache;
        _userRepository = userRepository;
    }

    public User RetrieveUserById(int userId)
    {
        var buildCacheKey = UserService.BuildCacheKey(userId);

        return _cache.RetrieveOrAdd(BuildCacheKey, () => { return _userRepository.RetrieveUserBy(userId); });
    }

    public void Delete(int userId)
    {
        var buildCacheKey = UserService.BuildCacheKey(userId);

        _userRepository.Delete(userId);
        _cache.Remove(BuildCacheKey(userId));
    }

    private static string BuildCacheKey(int userId)
    {
        return $"user_{userId}";
    }
}

以上です!リクエストキャッシングは、必要な場所ならどこでも注入可能になりました。

Gitリポジトリにアクセスして、コードを試してみてください。

Author: Chuck Conway is an AI Engineer with nearly 30 years of software engineering experience. He builds practical AI systems—content pipelines, infrastructure agents, and tools that solve real problems—and shares what he’s learning along the way. Connect with him on social media: X (@chuckconway) or visit him on YouTube and on SubStack.

著者: Chuck Conwayは、ソフトウェアエンジニアリングの経験が30年近くあるAIエンジニアです。彼は実用的なAIシステム(コンテンツパイプライン、インフラストラクチャエージェント、実際の問題を解決するツール)を構築し、学んだことを共有しています。ソーシャルメディアで彼とつながってください: X (@chuckconway) または YouTubeSubStack で彼を訪問してください。

↑ トップに戻る

こちらもおすすめ