簡體   English   中英

存儲庫模式和本地緩存

[英]Repository Pattern and Local Caching

我有以下接口/類:

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
}

它改編自網上的各種圖案。 我不認為這對於具有數十萬行的表有用,但對於查找表等 - 我並不總是打到數據庫。

它可以工作,但有些東西不是很干凈,例如我刷新緩存 - 有時我必須再次拉取所有數據(目前正在進行中)。

這個聲音設計? 還是我在這里重新發明輪子?

一個有趣的問題+1。 在我看來,上下文內容緩存是最好的,或者單獨使用。 並使用DB緩存。

為什么:

  • 並行WP都有緩存
  • 每個WP都可能有線程,上下文不是線程安全的
  • 每個線程應該有緩存嗎?
  • 您的緩存會話是否持久?
    • 否:您重新加載每個請求
    • 是的:您在ASP.NET,EnterpriseLibary緩存或類似的地方使用全局緩存?
      • 您是否正確管理緩存?
      • 你如何處理並發和變化
  • 您是否考慮過Context生命周期的最佳實踐? 一些專家建議只有短暫的壽命
  • DB位於WebServer附近的LAN上嗎?
  • 您是否比較了使用DB緩沖區訪問時的響應時間?

在各種環境中研究了這個主題,而不僅僅是EF / .NET / SQL Server,我得出的結論是,除非數據庫服務器已成為或者趨向於成為CPU瓶頸並且無法輕松擴展,否則它是為DB提供內存並讓它在構建或嘗試緩存條目之前緩存100sMB的一種非常合理的方法。 在使用WebServer上的app knots進行編碼之前,我寧願在SQL Server中拋出GB或RAM。

當每微秒計數時,或者您的數據庫在網絡范圍內與延遲/吞吐量問題分離,並且您的數據是非易失性的,並且不需要緩存到期/並發管理。 然后繼續實施緩存。

仔細考慮內存使用,緩存構建時間和內存持久性模型。

查看一些用於緩存創意和潛在解決方案的工具。 例如企業緩存塊。

祝好運。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM