C# 緩存 二

源代碼: https://git.oschina.net/zhaord/CacheDemo.git (分支:dev_entitycache)


介紹

經(jīng)過(guò) 《C# 緩存》《C#使用redis》作為緩存,我們可以了解C#中簡(jiǎn)單使用緩存的原理和代碼。
使用緩存,可以降低操作數(shù)據(jù)庫(kù)頻率進(jìn)而提升性能。在使用緩存過(guò)程中,我們會(huì)遇到一個(gè)問(wèn)題,就是做數(shù)據(jù)庫(kù)操作的時(shí)候,當(dāng)我們對(duì)數(shù)據(jù)庫(kù)進(jìn)行了改動(dòng),即進(jìn)行增加刪除更新操作的時(shí)候,我們?nèi)绾翁幚砭彺鏀?shù)據(jù)?1.改變改變數(shù)據(jù)庫(kù)時(shí)改變緩存,2.改變數(shù)據(jù)庫(kù)時(shí)情況緩存。我推薦后者,因?yàn)?,第一種方案,是對(duì)兩個(gè)數(shù)據(jù)源進(jìn)行操作,這會(huì)牽扯到事務(wù)等等,提高了復(fù)雜性,而第二種,只有當(dāng)我操作數(shù)據(jù)庫(kù)成功時(shí),才會(huì)去清空緩存數(shù)據(jù)。


原理

在上述兩篇文章中,我們發(fā)現(xiàn),不管是內(nèi)存緩存,還是redis緩存,均可對(duì)緩存起一個(gè)別稱,即Name字段。
既然,我們可以對(duì)緩存起別名,也就是說(shuō),我們可以針對(duì)一個(gè)實(shí)體,或者說(shuō)一個(gè)存儲(chǔ)模型,根據(jù)類名或者其他的唯一命名方式,對(duì)這個(gè)實(shí)體的查詢操作,建立緩存,在進(jìn)行 增加 更新 刪除操作的時(shí)候,情況這個(gè)別名的緩存信息。


代碼實(shí)現(xiàn)

1.實(shí)體基類和具體的實(shí)體

public class Entity
    {
        public Guid Id { get; set; }

        public void GenerateId()
        {
            this.Id=Guid.NewGuid();
        }
    }

  public class DemoCacheEntity:Entity
    {
        public int RandomValue { get; set; }
    }

  1. 新建提示倉(cāng)儲(chǔ)接口和實(shí)現(xiàn)

    public interface IRepository<TEntity>
        where TEntity:Entity
    {
        void Add(TEntity entity);

        void Update(TEntity entity);

        void Remove(TEntity entity);

        TEntity Get(Guid id);   
    }

    public class Repository<TEntity>
        :IRepository<TEntity>
        where TEntity : Entity
    {
        /// <summary>
        ///     保存 entities 的地方
        /// </summary>
        private static IList<TEntity> entities=
            new List<TEntity>();

        private readonly ICache _cache;

        public Repository(ICache cache)
        {
            this._cache = cache;
        }

        public void Add(TEntity entity)
        {
            if (entity.Id == Guid.Empty)
            {
                entity.GenerateId();
            }
            entities.Add(entity);
            this._cache.Clear();

        }

        public void Update(TEntity entity)
        {
            var toUpdate = entities.FirstOrDefault();
            toUpdate = entity;
            this._cache.Clear();
        }

        public void Remove(TEntity entity)
        {
            var toDelete = entities.FirstOrDefault();
            entities.Remove(toDelete);
            this._cache.Clear();
        }

        public TEntity Get(Guid id)
        {
            var key = id.ToString();
            return (TEntity)this._cache.Get(key,
                (k) => this.GetFromList(id));   
        }

        private TEntity GetFromList(Guid id)
        {
            var entity = entities.FirstOrDefault(c => c.Id == id);
            Console.WriteLine("如果顯示了這條信息,表示從list中取數(shù),而非緩存中取數(shù)據(jù)");
            return entity;
        }
    }

通過(guò)倉(cāng)儲(chǔ)的實(shí)現(xiàn),我們可以發(fā)現(xiàn),在通過(guò)構(gòu)造函數(shù),傳入 緩存,在 add update delete 方法中,對(duì)緩存進(jìn)行清空處理,get方法是默認(rèn)的緩存處理手段, 接下來(lái),看democacheentity的具體倉(cāng)儲(chǔ)實(shí)現(xiàn)方式

public class DemoEntityRepository
        : Repository<DemoCacheEntity>, IRepository<DemoCacheEntity>
    {

        private static ICache DemoEntityCacheInstance = new DemoCache("ENTITYCACHE-DEMOCACHEENTITY");

        public DemoEntityRepository()
            :base(DemoEntityCacheInstance)
        {
            // 每個(gè)實(shí)體對(duì)應(yīng)的 cache name 應(yīng)該是唯一的
        }
    }

具體的倉(cāng)儲(chǔ)實(shí)現(xiàn)方式就比較簡(jiǎn)單了,就是建立倉(cāng)儲(chǔ)實(shí)例的時(shí)候,傳入一個(gè)唯一的緩存實(shí)現(xiàn)。
到此,我們就可以通過(guò) 管理名稱為"ENTITYCACHE-DEMOCACHEENTITY"來(lái)管理實(shí)體為DemoCacheEnttiy的緩存。


使用

1.插入時(shí)的使用方式

Console.WriteLine("----------------------測(cè)試 Add Start----------------------");
            IRepository<DemoCacheEntity> repository=
                new DemoEntityRepository();
            
            var testId = Guid.NewGuid();

            var entity=new DemoCacheEntity()
                           {
                Id = testId,
                               RandomValue = GetRandomValue()
            };
            repository.Add(entity);

            DemoCacheEntity getEntity = repository.Get(testId);
            Console.Write("第一次取值   ");
            ShowEntityCacheItem(getEntity);

            Stopwatch watch = new Stopwatch();
            watch.Start();

            while (true)
            {
                if (watch.ElapsedMilliseconds < 10000)
                {
                    continue;
                }

                getEntity = repository.Get(testId);
                Console.Write("10s后取值    ");
                ShowEntityCacheItem(getEntity);
                break;
            }

            while (true)
            {
                if (watch.ElapsedMilliseconds < 40000)
                {
                    continue;
                }
                getEntity = repository.Get(testId);
                Console.Write("40s后取值    ");
                ShowEntityCacheItem(getEntity);
                break;
            }

            while (true)
            {
                if (watch.ElapsedMilliseconds < 70000)
                {
                    continue;
                }
                getEntity = repository.Get(testId);
                Console.Write("70s后取值  此處應(yīng)該還是entity的值 ");
                ShowEntityCacheItem(getEntity);
                break;
            }

            watch.Stop();
            Console.WriteLine("----------------------測(cè)試 Add End----------------------");
            

插入運(yùn)行結(jié)果

圖片.png

通過(guò)運(yùn)行結(jié)果,我們不難發(fā)現(xiàn),70s之后,即緩存到期,數(shù)據(jù)是再次從列表中過(guò)去

2.更新實(shí)體屬性

Console.WriteLine("----------------------測(cè)試 Update Start----------------------");

            IRepository<DemoCacheEntity> repository =
                new DemoEntityRepository();


            DemoCacheEntity getEntity = repository.Get(testId);
            Console.Write("第一次需要update的信息,此處應(yīng)該是緩存信息,沒(méi)有打印從list取值信息  ");
            ShowEntityCacheItem(getEntity);

            getEntity.RandomValue = GetRandomValue();
            repository.Update(getEntity);

            getEntity = repository.Get(testId);
            Console.Write("第一次需要update的信息,此處應(yīng)該是list信息,打印從list取值信息  ");
            ShowEntityCacheItem(getEntity);

            Stopwatch watch = new Stopwatch();
            watch.Start();

            while (true)
            {
                if (watch.ElapsedMilliseconds < 10000)
                {
                    continue;
                }

                getEntity = repository.Get(testId);
                Console.Write("10s后取值    ");
                ShowEntityCacheItem(getEntity);
                break;
            }

            while (true)
            {
                if (watch.ElapsedMilliseconds < 40000)
                {
                    continue;
                }
                getEntity = repository.Get(testId);
                Console.Write("40s后取值    ");
                ShowEntityCacheItem(getEntity);
                break;
            }

            while (true)
            {
                if (watch.ElapsedMilliseconds < 70000)
                {
                    continue;
                }
                getEntity = repository.Get(testId);
                Console.Write("70s后取值  此處應(yīng)該還是entity的值 ");
                ShowEntityCacheItem(getEntity);
                break;
            }

            watch.Stop();
            Console.WriteLine("----------------------測(cè)試 Update End----------------------");
            

更新實(shí)體,運(yùn)行結(jié)果

圖片.png

從運(yùn)行結(jié)果可以看出,當(dāng)我們更新實(shí)體后,是在次重list中獲取數(shù)據(jù),而非從緩存中獲取數(shù)據(jù)

  1. 刪除實(shí)體
Console.WriteLine("----------------------測(cè)試 Delete Start----------------------");
            IRepository<DemoCacheEntity> repository =
                new DemoEntityRepository();
            DemoCacheEntity getEntity = repository.Get(testId);
            Console.Write("第一次需要Delete的信息,此處應(yīng)該是緩存信息,沒(méi)有打印從list取值信息  ");
            ShowEntityCacheItem(getEntity);
            repository.Remove(getEntity);
            Stopwatch watch = new Stopwatch();
            watch.Start();

            while (true)
            {
                if (watch.ElapsedMilliseconds < 10000)
                {
                    continue;
                }

                try
                {
                    getEntity = repository.Get(testId);
                    if (getEntity == null)
                    {
                        Console.WriteLine("從數(shù)據(jù)庫(kù)中刪除了");
                        break;
                    }
                    
                }
                catch (Exception ex)
                {
                    Console.WriteLine("從數(shù)據(jù)庫(kù)中刪除了");
                    break;
                }
            }
            watch.Stop();
            Console.WriteLine("----------------------測(cè)試 Delete End----------------------");

刪除操作運(yùn)行結(jié)果

圖片.png

根據(jù)運(yùn)行結(jié)果,我們可以看出,當(dāng)我們刪除實(shí)體后,再次嘗試讀取數(shù)據(jù)時(shí),會(huì)提示已經(jīng)刪除,也不會(huì)從list中讀取數(shù)據(jù)。


總結(jié)

根據(jù)以上結(jié)果,我們可以得出一個(gè)結(jié)論,就是我們可以通過(guò)管理唯一命名的緩存來(lái)管理實(shí)體緩存,不需要去更新緩存數(shù)據(jù)。
作為緩存,就兩個(gè)操作,要么生效,要么失效,我們可以在取數(shù)據(jù)的時(shí)候,讓緩存生效,在改動(dòng)數(shù)據(jù)庫(kù)的時(shí)候,讓緩存失效,即可達(dá)到當(dāng)數(shù)據(jù)庫(kù)數(shù)據(jù)發(fā)生改變的時(shí)候,我們的緩存數(shù)據(jù),得到的永遠(yuǎn)是數(shù)據(jù)庫(kù)值(直接修改數(shù)據(jù)庫(kù)不會(huì)清空緩存)。
擴(kuò)展1:這里是在倉(cāng)儲(chǔ)中操作了緩存,倉(cāng)儲(chǔ)依賴了緩存,是個(gè)不好的做法,可以借助領(lǐng)域事件來(lái)完成情況緩存的操作,而且,緩存不應(yīng)該是在倉(cāng)儲(chǔ)中使用,合理的使用,應(yīng)該是在應(yīng)用層和倉(cāng)儲(chǔ)之間,建立緩存體系。
擴(kuò)展2:這里建立緩存,是通過(guò)唯一指定的名稱建立緩存組件,其實(shí),我們可以不指定唯一名稱,而是根據(jù)提示的type,獲取到名稱,來(lái)建立對(duì)應(yīng)的緩存組件。


QQ:1260825783
轉(zhuǎn)載請(qǐng)注明出處!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容