🚫 Ad Blocker Detected

Please disable your AD blocker to continue using this site. Ads help us keep the content free! please press keyboard F5 to refresh page after disabled AD blocker

請關閉廣告攔截器以繼續使用本網站。廣告有助於我們保證內容免費。謝謝! 關閉後請按 F5 刷新頁面

0%

Asp.net使用快取 (二)

Asp.net使用快取 (一)

向大家簡單介紹

  1. 快取是什麼
  2. 為何要使用快取
  3. 使用簡單HttpRuntime.Cache使用快取機制

這篇是分享把快取程式碼變得更有彈性


第二篇大綱

  1. 提出介面,提高可替換性
  2. 使用泛型改寫快取 讀取方式
  3. 使用擴充方法改寫快取

提出介面,提高可替換性

情境:

目前有個專案使用 HttpRuntime.Cache 物件

在記憶體快取中除了使用 Asp.Net 中HttpRuntime.Cache類別外還有很多解決方案.例如使用Memcache,Redis

如果我們原本使用HttpRuntime.Cache類別但之後要轉成其他快取方式怎麼辦?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HomeController : Controller
{
System.Web.Caching.Cache cacheContainer = HttpRuntime.Cache;

public ActionResult Index()
{

string cacheData = cacheContainer.Get("data") as string;

if (cacheData==null)
{
cacheContainer.Insert("test1", DateTime.Now.ToShortDateString());
}

return View(cacheData);
}
}

雖然使用不同快取方式,但記得我上篇的重點快取會有兩個動作,讀和寫,所以最基本就會有讀和寫這兩個動作

OOP有個很重要的觀念 多個類有重複動作考慮提出父類別

為了方便了解我把HttpRuntime.Cache封裝成一個類別

1
2
3
4
5
6
7
8
9
10
public class NetCache {
System.Web.Caching.Cache cacheContainer = HttpRuntime.Cache;
public object GetCacheObject(string key) {
return cacheContainer.Get(key);
}

public void SetCache(string key,object obj) {
cacheContainer.Insert(key, obj);
}
}

這邊有另一個Memcache快取Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MemeryCache {
private ObjectCache _cache = MemoryCache.Default;
public object GetCacheObject(string key)
{
return _cache[cacheKey];
}

public void SetCache(string key, object obj)
{
var policy = new CacheItemPolicy();
policy.RemovedCallback = OnFileContentsCacheRemove;
// 設定快取時間2分鐘
policy.AbsoluteExpiration = DateTimeOffset.Now.Minute(2);
_cache.Set(cacheKey, fileContents, policy);
}
}

先不關注這兩個物件裡面細節,我們可以發現他們都有 GetCacheObject 方法和SetCache 方法

這時我們就可以適時提出介面(interface),當作這兩個類別的合約

1
2
3
4
5
6
public interface ICache {

void Set(string key,object obj);

object Get(string key);
}

之後將他們兩個類別實現 ICache 介面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MemeryCache : ICache
{
private ObjectCache _cache = MemoryCache.Default;
public object Get(string key)
{
return _cache[cacheKey];
}

public void Set(string key, object obj)
{
var policy = new CacheItemPolicy();
policy.RemovedCallback = OnFileContentsCacheRemove;
// 設定快取時間2分鐘
policy.AbsoluteExpiration = DateTimeOffset.Now.Minute(2);
_cache.Set(cacheKey, fileContents, policy);
}
}

public class NetCache : ICache
{
System.Web.Caching.Cache cacheContainer = HttpRuntime.Cache;
public object Get(string key) {
return cacheContainer.Get(key);
}

public void Set(string key,object obj) {
cacheContainer.Insert(key, obj);
}
}

提出介面有甚麼好處?

我們可以把前面程式碼改成IOC依賴注入的方式,不要在程式碼寫死使用HttpRuntime.Cache,由IOC容器幫我們把物件注入程式碼中.

Note:我使用建構子注入法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HomeController : Controller
{
//不用寫死使用 HttpRuntime.Cache
//System.Web.Caching.Cache cacheContainer = HttpRuntime.Cache;
ICache cacheContainer;
public HomeController(ICache Container){
cacheContainer = Container;
}

public ActionResult Index()
{

string cacheData = cacheContainer.Get("data") as string;

if (cacheData==null)
{
cacheContainer.Insert("test1", DateTime.Now.ToShortDateString());
}

return View(cacheData);
}
}

ICache 變成快取程式碼的潤滑劑.可讓程式變得更有彈性


使用泛型改寫快取 讀取方式

我在StackOverFlow解答的方式就是第二種

其中最主要的技巧就是把Get方法返回的Object改成使用泛型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 public T GetOrSetCache<T>
(string key,T obj, int cacheTime) where T:class,new()
{
System.Web.Caching.Cache cacheContainer = HttpRuntime.Cache;
T cacheObj = cacheContainer.Get(key) as T;

if (cacheObj == null)
{
cacheContainer.Insert(key,
obj,
null,
DateTime.Now.AddMinutes(cacheTime),
System.Web.Caching.Cache.NoSlidingExpiration);
cacheObj = obj;
}

return cacheObj;
}

讓我們在使用時可以變成

1
2
3
var data = DateTime.Now.ToShortDateString();
int numberOfMinutes = 3;
data = GetOrSetCache("name1",data,numberOfMinutes );

我們只需要呼叫GetOrSetCache方法,這個方法把GetCacheSetCache封裝起來了


使用擴充方法改寫快取

.Net有提供一個很方便的機制 擴充方法,這個機制幫我們解決一個很重要的問題.
我們可以擴充已經封裝但沒有原始碼的類別,

在這段程式碼中,使用Func<TObj> 可以使用lambda 表達式,讓程式碼更簡潔有力!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static TObj GetOrSetCache<TObj>(this Func<TObj> selector, string key, int cacheTime)    where TObj : class
{
Cache cacheContainer = HttpRuntime.Cache;
//get cache Object
var obj = cacheContainer.Get(key) as TObj;

//if there isn't cache object add this object to cache
if (obj == null)
{
obj = selector();
cacheContainer.Insert(key, obj);
}

return obj;
}

我們使用時如下

變更簡潔動作更漂亮

1
2
int numberOfMinutes = 3;
data = GetOrSetCache(()=> DateTime.Now.ToShortDateString(),"name1",data,numberOfMinutes );

同場加映:

擴展方法和介面搭配使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

public class WebDefaultCache : ICache
{
Cache cacheContainer = HttpRuntime.Cache;
public object Get(string key)
{
return cacheContainer.Get(key);
}

public void Set(string key, object obj)
{
cacheContainer.Insert(key, obj);
}
}
public interface ICache{
void Set(string key, object obj);

object Get(string key);
}

public static class InfrastructureExtension
{
public static TObj GetOrSetCache<TObj>(this Func<TObj> selector, string key) where TObj : class {
return GetOrSetCache(selector, key,10);
}

public static TObj GetOrSetCache<TObj>(this Func<TObj> selector, string key, int cacheTime) where TObj : class
{
return GetOrSetCache(selector, key, cacheTime, new WebDefaultCache());
}

public static TObj GetOrSetCache<TObj>(this Func<TObj> selector, string key, int cacheTime, ICache cacheContainer) where TObj : class
{
//get cache Object
var obj = cacheContainer.Get(key) as TObj;

//if there isn't cache object add this object to cache
if (obj == null)
{
obj = selector();
cacheContainer.Set(key, obj);
}

return obj;
}
}

雖然在使用上和第三種一樣
但我們多了使用方法重載多傳一個參數ICache介面 可以讓我們在寫程式時決定要使用哪種cache方式,不用改快去那邊程式碼.

同場加映程式碼我放在我自己常用的ExtenionTool專案中

__此文作者__:Daniel Shih(石頭)
__此文地址__: https://isdaniel.github.io/cache-asp-net-2/
__版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!

如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^