保持缓存与后端同步

时间:2013-03-11 07:54:48

标签: c# caching repository

请记住,我在这里展示的示例仅用于尽可能清楚地解释问题,而不是任何实际的实现或生产代码。

另外,我们假设如果从后端存储或返回任何内容,它将被缓存。我在代码中省略了如何以及在何处发生的示例。

对于这个问题的范围,我们还必须假设,MyType集合总是很小,相当静态,当应用程序启动时,它将从后端获取所有内容并重用缓存副本直到应用程序关闭。这就是GetCached(id)GetBackend(id)实际上只是ListCachedListBackend的包装的原因。

假设我们有以下类型的简单存储库:

public class MyRepository : IRepository<MyType>
{
    public IEnumerable<MyType> GetAll()
    {
        // Will return cached items if present
        return ListCached ?? ListBackend;
    }

    public MyType Get(int id)
    {
        return GetCached(id) ?? GetBackend(id);
    }


    private MyType GetBackend(int id)
    {
        return ListBackend.FirstOrDefault(type => type.Id == id);
    }

    private MyType GetCached(int id)
    {
        return ListCached.FirstOrDefault(type => type.Id == id);
    }

    protected IEnumerable<MyType> ListBackend
    {
        get { return Backend.GetAll<MyType>(); }
        set { Backend.StoreAll<MyType>(value); }
    }

    public IEnumerable<MyType> ListCached
    {
        get { return Cache.GetAll<MyType>(); }
        set { Cache.StoreAll<MyType>(value); }
    }

    public void Store(MyType value)
    {
        Backend.Store(value);
    }
}

这就是挑战:

class Program
{
    static void Main(string[] args)
    {
        #region Handling Missing Objects in Cache
        // We have a repository
        var rep = new MyRepository();

        // Into which we put stuff (3 for the demo)
        rep.Store(new MyType { Id = 1 });
        rep.Store(new MyType { Id = 2 });
        rep.Store(new MyType { Id = 3 });

        // And the repository, after this, returns 3 items
        // The items are returned from cache
        var count = rep.GetAll().Count(); // Returns 3

        // However, somewhere else in the application, it happens so,
        // for any reason, bug, programmer error, photon from sun hitting the cpu
        // or tinfoil-hat left home in rush,
        // that one item gets removed from the cache
        Cache.Remove(new MyType { Id = 2 });

        // After which, only 2 items are returned from the repository
        // since the cache exists, it won't even try to hit the database
        count = rep.GetAll().Count();

        // Now count = 2, while WE know the backend has now 3 items
        // how would the program detect it and get a fresh copy from backend?
        #endregion
    }
}

在这种情况下你会做什么?是否存在有助于检测情况并从后端获取新鲜集合的模式。什么是最佳实践?

2 个答案:

答案 0 :(得分:1)

恕我直言,

1-如果您没有在数据库之间添加,那么您可以保留一个计数器并在启动应用程序时填充它。

2-如果您可以将List更改为可观察的集合,该集合在更新时进行更新,那么当项目被删除时,您将收到通知,您可以与计数器核实是否可以重新加载它。

3-如果无法使用可观察的集合,那么你可以使用CacheExpiray策略的概念,在这个策略中你可以模拟一段时间后会从源重新加载的缓存。

答案 1 :(得分:0)

可能有一些选择。

首先,使用到期缓存。只需为当前缓存设置特定的过期即可。如果时间已过,则刷新数据。但是,这种模式不能

if(cacheExpiry){
  cacheList = GetListFromDatabase();
}

但是,如果您想确保获取最新数据,通常我会使用此方法:

1,设置生成缓存时的当前时间戳。一些例子:

DateTime lastCache = DateTime.Now

然后在你的sql中,有一个包含最新修改日期时间的表(例如UPDATE_STAMP)。然后,每次修改所需的缓存表时,请将UPDATE_STAMP表中的记录更新为当前日期。例如:

Update MyTable set //your code here
Update UPDATE_STAMP set DATE_TIME = GETDATE()

然后每次获得ListCached时,将最新的缓存时间与UPDATE_STAMP表记录进行比较。例如:

public IEnumerable<MyType> ListCached
{
    get { 
      DateTime latestModified = GetLatestModifiedDate();
      if(lastCache<latestModified) Cache = GetListFromDatabase();
      latestModified = DateTime.Now;
      return Cache.GetAll<MyType>();
    }
}