源代碼: 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; }
}
- 新建提示倉(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é)果

通過(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é)果

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

根據(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)注明出處!