インメモリキャッシュに対する私の見解

· 2分で読める

私は大量のデータを処理するものに取り組んできました。やっていくうちに、その大部分は決して変わらない、少なくとも一定期間は変わらないことに気づきました。

そこで、個人用のキャッシュ リポジトリを作成すると便利だと思いました。もちろん、これは新しいことではありません。数週間前、アプリケーション キャッシュの管理方法について、Nick Craver によって書かれた StackOverflow の 投稿 でこれについて読みました。

また、私は常にキャッシュを扱いたいと思っていました。キャッシュがどのように機能するのか、ロジック、そしてどうすればキャッシュを機能させることができるのか、だから… やるべきではありません!

フロー

ユーザーが値を要求したときに、キャッシュを制御するクラスがどのように動作するかを示す簡単なフローを次に示します。

Gyazo からの画像

実装

始める前に

わかってる、わかってる。メモリ キャッシュを処理する System.Runtime.Caching があります。しかし、私はそれを自分で構築することにしました。そのクラスを使用したい場合は、ここ でハウツーを確認してください。

キャッシュアイテム

最初のステップは、オブジェクトの値と有効期限を格納するクラスを作成することです。おそらくもっと良い方法があると思いますが、私が考えたのはこれですので、次のとおりです。

public class CacheItem
{
    public string Identifier { get; set; }
    public object Value { get; set; }
    public DateTime ValidUntil { get; set; }

    public CacheItem(string identifier, object value, TimeSpan valid)
    {
        Identifier = identifier;
        Value = value;
        ValidUntil = DateTime.UtcNow.Add(valid);
    }
}

キャッシュリポジトリ

次に、オブジェクトを処理してどこかに保存するクラスが必要になります (CacheItem として)。私はサフィックス Repository を持つクラス内のすべてのデータ/モデルを処理したいと考えていますが、しかしそうする必要はありません。そこで、キャッシュ用にデータ/モデルを構築しましょう。

public class CacheRepository
{
    private static Dictionary<string, string> Cache = new Dictionary<string, string>();
    private static T Set<T>(string key, Func<T> lookup, TimeSpan durationMinutes)
    {
        var item = new Models.Item(key, lookup(), durationMinutes);
        return Save<T>(key, item);
    }
    private static T Save<T>(string key, Item item)
    {
        Cache[key] = JsonConvert.SerializeObject(item);
        return (T)item.Value;
    }
    private static T Get<T>(string key)
    {
        var cached = Cache.FirstOrDefault(x => x.Key == key).Value;
        var item = string.IsNullOrEmpty(cached) ? null : JsonConvert.DeserializeObject<Item>(cached);
        return (item == null) 
        ? default : (item.ValidUntil > DateTime.UtcNow) 
        ? JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(item.Value)) : default;
    }
    public static T GetOrSet<T>(string key, Func<T> lookup, TimeSpan durationMinutes)
    {
        var cache = Get<T>(key);
        return cache == null ? Set(key, lookup, durationMinutes) : cache;
    }
}

これまでのところ、Cache という名前の静的ディクショナリがあり、そこにすべての項目が保存されます。これはアプリケーションの実行中にのみ持続することに注意してください。そのため、この チュートリアル タイトルにはメモリ内キャッシュが含まれています。

CacheRepository クラスがロードされると、Cache 項目が初期化されることに注意してください。

CacheRepository クラスを呼び出すときに使用できる唯一のメソッドは、3 つのパラメーターを必要とする GetOrSet(string key, Func<T> lookup, TimeSpan durationMinutes) です。

  1. key: 保存するオブジェクトの識別子。
  2. lookup: キャッシュの有効期限が切れた場合、またはキャッシュが null の場合のコールバック関数。
  3. durationMinutes: 現在時刻 (UTC) に追加される期間 (分単位)。

キャッシュにかかる時間

次に、キャッシュ リポジトリを使用して、どこかからデータを取得します。

これらすべてを理解するために、いくつかのプロパティを含むサンプル オブジェクトを作成し、そのオブジェクトのリストをフェッチして入力するリポジトリを作成しましょう。

public class User 
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}
public class UserRepository 
{
    public List<User> Get()
    {
        // Code to get users
    }
}

User のリストを埋めるメソッドがあるので、CacheRepository クラスを利用しましょう。

private UserRepository _userRepository = new UserRepository();
private List<User> _user;
public List<User> User
{
    get
    {
        _user = CacheRepository.GetOrSet($"users", usersRepo.Get, TimeSpan.FromMinutes(10));
        return _user;
    }
}

同様に、変数 User にアクセスするたびに、キー users を持つオブジェクトの値を CacheRepository に要求します。

そのキーが存在する場合は、有効期限がチェックされます。これらの条件のいずれかが false の場合、コールバックを使用してオブジェクトの値を (usersRepo.Get で) 設定し、有効期限を DateTime.UtcNow + TimeSpan.FromMinutes(10) に設定してキャッシュに保存して返します。