2013-07-25 49 views
5

私は、次のインタフェース/クラスを持っている:リポジトリパターンとローカルキャッシュ

public interface IUnitOfWork : IDisposable 
{ 
    event EventHandler<EventArgs> Saved; 
    DbSet<T> Set<T>() where T : class; 
    DbEntityEntry<T> Entry<T>(T entity) where T : class; 
    void Commit(); 
} 

とリポジトリの実装:

public class CachedSqlRepository<T, TKey, TContext> : ICacheRepository<T, TKey, TContext> 
    where T : class 
    where TContext : DbContext, IDisposable, new() 
{ 
    //A list of the Navigation Properties to include 
    private readonly Expression<Func<T, object>>[] _NavigationProperties; 

    public CachedSqlRepository(params Expression<Func<T, object>>[] navigationProperties) 
    { 
     _NavigationProperties = navigationProperties; 
     using (TContext dbContext = new TContext()) //Fetch the List of Entities 
     { 
      RefreshCache(dbContext); 
     } 
    } 
    /// <summary> 
    /// The Collection of Items in the database 
    /// Note this is a Cache, but should replicate whats in the DB 
    /// </summary> 
    public IList<T> Items { get; private set; } 

    public bool Any(Func<T, bool> predicate) 
    { 
     return Items.Any(predicate); 
    } 

    public void RefreshCache(DbContext context) 
    { 
     switch (_NavigationProperties.Length) 
     { 
      case 0: 
       Items = context.Set<T>().ToList(); 
       break; 
      case 1: 
       Items = context.Set<T>().Include(_NavigationProperties[0]).ToList(); 
       break; 
      //more here 
     } 
    } 

    /// <summary> 
    /// Refresh the internal cache 
    /// </summary> 
    public void RefreshCache() 
    { 
     using (TContext dbContext = new TContext()) 
     { 
      RefreshCache(dbContext); 
     } 
    } 

    public IEnumerable<T> FilterBy(Func<T, bool> predicate) 
    { 
     return Items.Where(predicate); 
    } 

    public T Add(T entity) 
    { 
     T newEntity; 
     using (TContext dbContext = new TContext()) 
     { 
      newEntity = dbContext.Set<T>().Add(entity); 
      if (dbContext.SaveChanges() == 1) //1 change was made 
       Items.Add(newEntity); 
     } 
     return newEntity; 
    } 

    public void Delete(TKey id) 
    { 
     using (TContext dbContext = new TContext()) 
     { 
      var attachedEntry = dbContext.Set<T>().Find(id); 
      if (attachedEntry == null) return; //it doesnt exist anyway! 
      dbContext.Set<T>().Remove(attachedEntry); 
      dbContext.SaveChanges(); 
      RefreshCache(dbContext); 
     } 
    } 

    public void Update(T entity, TKey id) 
    { 
     if (entity == null) throw new ArgumentException("Cannot update a null entity."); 

     using (TContext dbContext = new TContext()) 
     { 
      var entry = dbContext.Entry(entity); 

      if (entry.State != EntityState.Detached) return; 
      T attachedEntity = dbContext.Set<T>().Find(id); 

      if (attachedEntity != null) 
      { 
       var attachedEntry = dbContext.Entry(attachedEntity); 
       attachedEntry.CurrentValues.SetValues(entity); 
      } 
      else 
      { 
       entry.State = EntityState.Modified; // This should attach entity 
      } 
      dbContext.SaveChanges(); 
      RefreshCache(dbContext); 
     } 
    } 

    #region Transaction Methods 
    public IUnitOfWork StartTransaction() 
    { 
     return new EFUnitOfWork(new TContext()); 
    } 

    public T TransactionAdd(T entity, IUnitOfWork context) 
    { 
     context.Saved += OnSave; 
     return context.Set<T>().Add(entity); 
    } 

    public void TransactionDelete(TKey id, IUnitOfWork context) 
    { 
     var attachedEntry = context.Set<T>().Find(id); 
     if (attachedEntry == null) return; //it doesnt exist anyway 
     context.Saved += OnSave; 
     context.Set<T>().Remove(attachedEntry); 
    } 

    public void TransactionUpdate(T entity, TKey id, IUnitOfWork context) 
    { 
     if (entity == null) throw new ArgumentException("Cannot update a null entity."); 

     var entry = context.Entry(entity); 

     if (entry.State != EntityState.Detached) return; 
     T attachedEntity = context.Set<T>().Find(id); 

     if (attachedEntity != null) 
     { 
      var attachedEntry = context.Entry(attachedEntity); 
      attachedEntry.CurrentValues.SetValues(entity); 
     } 
     else 
     { 
      entry.State = EntityState.Modified; // This should attach entity 
     } 
     context.Saved += OnSave; 
    } 

    private void OnSave(object sender, EventArgs e) 
    { 
     RefreshCache(); 
    } 
    #endregion 
} 

これは、ネット上の様々なパターンから構成されています。私はこれが何十万行ものテーブルに役立つとは思っていませんが、ルックアップテーブルなどのためには、私はいつもDBを打つわけではありません。

これは機能しますが、キャッシュをリフレッシュするなど、いくつかのことはスーパークリーンではありません。すべてのデータを再度取り出す必要がある場合があります(現在進行中の作業)。

このサウンドデザインはありますか?あるいは、私はここでホイールを再発明していますか?

+2

Entity Frameworkは、すでにリポジトリと作業単位パターンを完全に実装しています。この質問への答えを見てください:http://stackoverflow.com/questions/5488313/organizationally-where-should-i-put-common-queries-when-using-entity-framework(この質問にも:http:// stackoverflow.com/questions/5762846/is-unitofwork-and-genericrepository-pattern-redundant-in-ef-4-1-code-first)。彼らが設計しているものには、EFのLinqバインディングを使用します。それらをラップすることは時間の無駄です:IM: –

+1

(もちろんこのアドバイスはキャッシュには言及しませんが、あなたが作成するキャッシュ機構は、貧弱なものの背後に隠すのではなく、既存のEFインターフェースで動作するはずです。 )。 –

+0

ありがとうございました。Merlyn、実際には私たちが元々持っていたCRUDメソッドを汎用化したかったので、本当の意味でリポジトリを使用していません。しかし良いリンク – Simon

答えて

3

興味深い質問+1。私の見解では、コンテクストコンテンツのキャッシングは、適切に行われた方が良いか、うまくいっているのです。そしてDBキャッシングを使用してください。なぜ

  • パラレルWPSすべてのスレッドを有していてもよく、各WPは、コンテキストはスレッドセーフではありません
  • キャッシュを持っている
  • 各スレッドはキャッシュを持っているべき
  • キャッシュセッションは永続ですか?
    • いいえ:[はい、各要求
    • をリロードしない:あなたは、世界的なASP.NETのキャッシュ、EnterpriseLibaryキャッシュまたは類似のでしょうか?
      • キャッシュを正しく管理していますか?
      • あなたはどのように同時性に対処し、
  • を変更しないあなたは、コンテキストの有効期間の周りのベストプラクティスを検討していますか?一部の専門家は短い生涯をお勧めします
  • DBはWebServerの近くのLANにありますか?
  • DBバッファアクセスを使用しているときの応答時間を比較しましたか?

EF/.NET/SQL Serverだけでなく、さまざまな環境でこのトピックを調べた結果、DBサーバーがCPUのボトルネックになっている、またはそうしない限り、私は結論に達しませんでした。容易にスケーリングすることができます。これは、DBにメモリを提供し、エントリをキャッシュする前に100sMB をキャッシュするのに非常に合理的なアプローチです。 私は、Webサーバー上のアプリケーションノットで自分自身をコーディングする前に、SQL ServerにGBやRAMを投げ捨ててしまいます。

マイクロ秒ごとにカウントするか、遅延/スループットの問題が発生したネットワークスパンでDBが分離され、データは不揮発性であり、キャッシュの有効期限/並行性管理は不要です。次に、キャッシングを実装します。

メモリ使用量、キャッシュ構築時間、およびメモリ持続性モデルを注意深く考慮してください。

アイデアや潜在的な解決策のためにキャッシュ用に作られたツールを見てください。例えばエンタープライズキャッシングブロック。

Good Luck。

+0

ありがとう、いくつかの良い情報。私がこれを実装したとき、私のアプリケーションはちょっと敏感でしたが、あなたが正しいです - 我々が使っているハードウェアは最先端ではありません。 – Simon

+0

btwスーパー汎用リポジトリベースは依然として良好なパターンです。ちょっと疑わしい疑似キャッシュです。 –

+0

そう、私は同意する、その少し半分が完了しました。私はそれが私にかかる場所を見て、そこに事前に実装された実装があるかどうか疑問に思っていただけです – Simon

関連する問題