2017-02-01 15 views
2

キャッシングされたデータが存在するかどうかを確認し、取得した場合、それがapi /データベースを呼び出してキャッシュしない場合は、より効率的な方法がありますか?このようなコードを何度も繰り返し実行するのは本当に非効率的です。Cでの一般的なキャッシング

List<Map> maps = new List<Map>(); 
List<Playlist> playlists = new List<Playlist>(); 

if (SingletonCacheManager.Instance.Get<List<Map>>("Maps") != null) 
{ 
    maps = SingletonCacheManager.Instance.Get<ListMap>>("Maps"); 
} 
else 
{ 
    maps = _mapRepository.FindBy(x => x.Active).ToList(); 
    SingletonCacheManager.Instance.Add<List<Map>>(maps, "Maps", 20); 
} 

if (SingletonCacheManager.Instance.Get<List<Playlist>>("Playlists") != null) 
{ 
    playlists = SingletonCacheManager.Instance.Get<List<Playlist>>("Playlists"); 
} 
else 
{ 
    var p = await _apiService.GetPlaylists(); 
    playlists = p.ToList(); 
    SingletonCacheManager.Instance.Add<List<Playlist>>(playlists, "Playlists", 20); 
} 

は、この可能性のようなものです:

List<Map> maps = this.CacheHelper.GetCachedItems<Map>(key, lengthoftime); 

、その後GetCachedItemsは、キャッシュされた項目のチェックを行うだろうし、それに応じて取得します。これは可能なようですが、キャッシュされたアイテムが存在しない場合、API /データベースからアイテムを取得する必要があります。

唯一の解決策は、渡された型のswitch文です。

switch(<T>type) 
{ 
    case<Map>: 
     return _mapRepository.FindBy(x => x.Active); 
    case<Playlist>: 
     return await _apiService.GetPlaylists(); 
} 

ありがとうございました。

+1

私は次のようなことをします:https://github.com/WiredUK/Wired.Caching/blob/master/Wired.Caching/InMemoryCache.cs#L52。ですから、基本的には、データを(データベースやAPIなどから)キャッシュ関数に渡すための関数を渡しており、必要な場合にのみ呼び出されます。 – DavidG

答えて

1

私の解決策は、ラムダ式としてキャッシュされたデータを取得する関数を渡すことです。このようにして、キャッシュメソッドはキャッシュとデリゲートの呼び出しを必要なときにだけチェックすることができます。たとえば:

public T Get<T>(string key, Func<T> getItemDelegate, int duration) where T : class 
{ 
    var cache = GetCache(); 

    var item = SingletonCacheManager.Instance.Get<ListMap>>(key) as T; 

    if (item != null) return item; 

    item = getItemDelegate(); 

    SingletonCacheManager.Instance.Add<T>(item, key, duration); 

    return item; 
} 

今、あなたは、このように一般的に入手関数を呼び出すことができます。

var maps = Get<List<Map>>(
    "Maps", 
    () => _mapRepository.FindBy(x => x.Active).ToList(), 
    20); 
+0

これはクールな答えです!私は家に帰るときに病気を試して実行します。 –

+0

このコードの周りにも 'lock'を追加するべきでしょう。キーの周りをベースにすることをお勧めしますが、一般的なオブジェクトをロックするとパフォーマンスが低下する可能性があります。 – DavidG

+0

あなたはOPにコメントをしたことを理解できませんでした。私はこれがまさに私が探しているものだと思います!これありがとう。 –

0

なぜ地図やプレイリストに異なるキャッシュを使用しないのですか?その場合、1つの基本抽象クラスを記述し、それぞれの抽象クラスからapiのデータを読み込むメソッドだけを上書きすることができます。

+0

興味深いアイデアは、これを行かせて、それがどのように感じるかを見るかもしれません。 –

+0

あなたがそこにタイプを使用することはできないので、スイッチを使ったあなたの考えはうまくいかないでしょう。 –

+0

それはちょうど私の頭の上にあったものだった。 –

1

あなたもこれを行うことができます。

public interface ICacheManager 
{ 
    IList<T> Get<T>(string name); 
    void Add<T>(IList<T> data, string Id, int lifeTime); 
} 

public class CacheHelper 
{ 
    private readonly Dictionary<Tuple<Type, string>, Func<IEnumerable<object>>> dataRetrievalFuncs; 
    private readonly ICacheManager cacheManager; 

    public CacheHelper(ICacheManager cacheManager) 
    { 
     this.cacheManager = cacheManager; 
     dataRetrievalFuncs = new Dictionary<Tuple<Type, string>, Func<IEnumerable<object>>>(); 
    } 

    public void Register<T>(string name, Func<IEnumerable<T>> selector) where T : class 
    { 
     dataRetrievalFuncs[new Tuple<Type, string>(typeof(T), name)] = 
      () => (IEnumerable<object>)selector(); 
    } 

    public IList<T> GetCachedItems<T>(string name, int lifeTime = 20) 
     where T : class 
    { 
     var data = cacheManager?.Get<T>(name); 

     if (data == null) 
     { 
      data = (dataRetrievalFuncs[new Tuple<Type, string>(
         typeof(T), name)]() as IEnumerable<T>) 
        .ToList(); 
      cacheManager.Add(data, name, lifeTime); 
     } 

     return data; 
    } 
} 

そして今、あなたが登録する必要があるだろうし各タイプのデータ検索機能を使用して、単純にヘルパーを使用します。

//Setting up the helper 
CacheHelper helper = new CacheHelper(SingletonCacheManager.Instance); 
helper.Register("Maps",() => _mapRepository.FindBy(x => x.Active)); 
helper.Register("PlayLists", ...); 

//Retrieving data (where it comes from is not your concern) 
helper.GetCachedItems<Map>("Maps"); 
helper.GetCachedItems<PlayList>("Playlists"); 

以下のコメントで指摘されているように、この解決策はデータの検索に使用される依存関係(_mapRepository)の寿命に問題があります。この問題を回避するには、この同じソリューションを使用することですが、明示的にデータ検索の現時点での依存性を渡す:

public class CacheHelper 
{ 
    private readonly Dictionary<Tuple<Type, string>, Func<object, IEnumerable<object>>> dataRetrievalFuncs; 
    private readonly ICacheManager cacheManager; 

    public CacheHelper(ICacheManager cacheManager) 
    { 
     this.cacheManager = cacheManager; 
     dataRetrievalFuncs = new Dictionary<Tuple<Type, string>, Func<object, IEnumerable<object>>>(); 
    } 

    public void Register<TEntity, TProvider>(string name, Func<TProvider, IEnumerable<TEntity>> selector) 
     where TEntity : class 
     where TProvider: class 
    { 
     dataRetrievalFuncs[new Tuple<Type, string>(typeof(TEntity), name)] = 
      provider => (IEnumerable<object>)selector((TProvider)provider) 
    } 

    public IList<TEntity> GetCachedItems<TEntity>(string name, object provider, int lifeTime = 20) 
     where TEntity : class 
    { 
     var data = cacheManager?.Get<TEntity>(name); 

     if (data == null) 
     { 
      data = (dataRetrievalFuncs[new Tuple<Type, string>( 
         typeof(TEntity), name)](provider) as IEnumerable<TEntity>) 
        .ToList(); 
      cacheManager?.Add(data, name, lifeTime); 
     } 

     return data; 
    } 

} 

今使用が若干異なる次のようになります。

//Setting up the helper 
CacheHelper helper = new CacheHelper(SingletonCacheManager.Instance); 
helper.Register("Maps", (MapRepository r) => r.FindBy(x => x.Active)); 

//Retrieving data (where it comes from is not your concern) 
helper.GetCachedItems<Map>("Maps", _mapRepository); 

は、この最後の解決策に注意してください。タイプセーフではありません。あなたは間違ってタイプされたproviderGetCachedItems<T>を渡すことができますが、これは残念です。

+0

同じタイプのキャッシュが2つある場合、これは壊れます。 OPsの例を使って、彼はアクティブマップを 'List DavidG

+0

@DavidG真実ですが、それは簡単に解決できます。 'Tuple 'は 'Type'ではなく辞書のキーとして使うことができます。私はそれを答えに加えます。それは良い点です。 – InBetween

+0

確かに。もちろんこれに関する別の問題は、関数に必要な依存関係がもはや存在しないことです。たとえば、dbコンテキストがクローズ/ディスポジションされている可能性があります。 – DavidG