关于 ASP.NET 内存缓存你需要知道的 10 点
緩存機(jī)制的主要目的是提高應(yīng)用程序的性能。作為 ASP.NET 開發(fā)人員,你可能會(huì)意識(shí)到 ASP.NET Web 窗體以及 ASP.NET MVC 可以使用 Cache 對象緩存應(yīng)用程序的數(shù)據(jù)。這通常被稱為服務(wù)器端數(shù)據(jù)緩存,并且常作為框架的內(nèi)置功能。雖然 ASP.NET Core 中并沒有這樣的 Cache 對象,但是你可以很容易地實(shí)現(xiàn)內(nèi)存緩存。本文將向你說明如何實(shí)現(xiàn)。
在進(jìn)一步閱讀之前,你先創(chuàng)建一個(gè)基于 Web 應(yīng)用程序項(xiàng)目模板的新的 ASP.NET Core 應(yīng)用程序。
然后按照下面提到的步驟逐一構(gòu)建和測試由內(nèi)存緩存提供的各種功能。
1. 內(nèi)存緩存需要在啟動(dòng)類 Startup 中啟用一下
不同于 ASP.NET Web 窗體和 ASP.NET MVC,ASP.NET Core 沒有內(nèi)置的 Cache 對象,可以拿來在控制器里面直接使用。 這里,內(nèi)存緩存時(shí)通過依賴注入來啟用的,因此第一步就是在 Startup 類中注冊內(nèi)存緩存的服務(wù)。如此,就得打開?Startup 類然后定位到 ConfigureServices() 方法,像下面這樣修改 ConfigureServices() 方法:
publicvoidConfigureServices(IServiceCollection services){? ? services.AddMvc();? ? ? ? services.AddMemoryCache();}
為了向你的應(yīng)用程序加入內(nèi)存緩存能力,你需要在服務(wù)集合上調(diào)用 AddMemoryCache() 方法。采用這種辦法就可以讓一個(gè)內(nèi)存緩存(它是一個(gè) IMemoryCache 對象)的默認(rèn)實(shí)現(xiàn)可以被注入到控制器中去。
2. 內(nèi)存緩存使用依賴注入來注入緩存對象
然后打開 HomeController 并對其進(jìn)行修改,如下所示:
publicclassHomeController:Controller{privateIMemoryCache cache;publicHomeController(IMemoryCache cache){this.cache = cache;? ? }? ? ....}
如你所見,上述代碼聲明了一個(gè) ImemoryCache 的私有變量。該變量會(huì)被構(gòu)造器中被賦值。構(gòu)造器會(huì)通過 DI(依賴注入)接收到緩存參數(shù),然后被存儲(chǔ)在本地變量總,提供后續(xù)使用。
3. 你可以使用 Set() 方法來在緩存中存東西
等你有了這個(gè) IMemoryCache 對象,就可以讀取或者向它寫入數(shù)據(jù)了。向緩存寫入數(shù)據(jù)項(xiàng)是相當(dāng)直接的。
publicIActionResultIndex(){? cache.Set("timestamp", DateTime.Now.ToString());returnView();}
上述代碼在 Index() 這個(gè) action 中設(shè)置了一個(gè)緩存項(xiàng)。這是通過使用 IMemoryCache 的?Set() 來完成的。Set() 方法的第一個(gè)參數(shù)是鍵名,用來標(biāo)識(shí)該數(shù)據(jù)項(xiàng)。第二個(gè)參數(shù)是鍵的取值。在此例中,我們存儲(chǔ)一個(gè)字符串的鍵和一個(gè)字符串的值,而你也可以存儲(chǔ)其它類型 (原生以及自定義的類型) 的鍵值對。
4. 你可以使用 Get 方法來從緩存中獲取到一個(gè)數(shù)據(jù)項(xiàng)
等你向緩存中添加好了數(shù)據(jù),也許會(huì)想要在應(yīng)用程序的其它地方去獲取到該數(shù)據(jù),可以用?Get() 來做到。如下代碼會(huì)告訴你如何來做這件事情。
public IActionResultShow(){stringtimestamp= cache.Get("timestamp");? return View("Show",timestamp);
}
上述代碼從 HomeController 的另外一個(gè)action(Show)那里獲取到了一個(gè)緩存的數(shù)據(jù)項(xiàng)。Get() 方法會(huì)指定數(shù)據(jù)項(xiàng)的類型以及它的鍵名。如果該數(shù)據(jù)項(xiàng)存在的話,就會(huì)被返回并且被賦值給 timestamp 這個(gè)字符串變量。然后這個(gè)?timestamp 的值就會(huì)被傳遞給 Show 視圖。
Show 視圖只是簡單地輸出了 timestamp 的值,如下所示:
TimeStamp : @Model
@Html.ActionLink("Go back","Index","Home")
為了對目前為止你所寫的代碼進(jìn)行一下測試,請運(yùn)行應(yīng)用程序。首先將瀏覽器導(dǎo)航至?/Home/Index ,這樣 timestamp 鍵就會(huì)被賦值。然后導(dǎo)航至 /Home/Show 并查看 timestamp 值是否會(huì)輸出。下圖所示是 Show() 這個(gè) action 運(yùn)行起來的一個(gè)例子。
5. 你可以使用 TryGet() 來檢查緩存中是否存在特定的鍵值
如果你觀察前面的示例,會(huì)發(fā)現(xiàn)每次你導(dǎo)航至 /Home/Index 的時(shí)候, 都會(huì)有一個(gè)新的 timestamp 被賦值給了緩存項(xiàng)。這是因?yàn)槲覀儾]有對此進(jìn)行檢查,規(guī)定只有在數(shù)據(jù)項(xiàng)不存在的時(shí)候才賦值。許多時(shí)候你都會(huì)想要這樣做的。這里有兩種辦法可以在 Index() 這個(gè) action 里面來做這樣的檢查。我們把兩種辦法都在下面列了出來。
//first wayif(string.IsNullOrEmpty(cache.Get("timestamp"))){? cache.Set("timestamp", DateTime.Now.ToString());}//second wayif(!cache.TryGetValue("timestamp",outstringtimestamp)){? ? cache.Set("timestamp", DateTime.Now.ToString());}
第一種辦法使用了你早先用過的同一個(gè) Get() 方法,這一次它被拿來跟 if 塊一起用。如果 Get() 不能在緩存中找到指定的數(shù)據(jù)項(xiàng),IsNullOrEmpty() 就會(huì)返回 true。而只有這時(shí)候 Set() 才會(huì)被調(diào)用,一次來添加數(shù)據(jù)項(xiàng)。
第二種辦法更加優(yōu)雅一點(diǎn)。它使用 TryGet() 方法來獲取一個(gè)數(shù)據(jù)項(xiàng)。TryGet() 方法會(huì)返回一個(gè)布爾值來指明數(shù)據(jù)項(xiàng)有沒有被找到。實(shí)際的數(shù)據(jù)項(xiàng)可以使用一個(gè)輸出參數(shù)拉取出來。如果 TryGet() 返回false,Set() 就會(huì)被用來添加數(shù)據(jù)。
6. 如果不存在的話,可以使用 GetOrCreate() 來添加一項(xiàng)
有時(shí)你需要從緩存中檢索現(xiàn)有項(xiàng)。如果該項(xiàng)目不存在,則希望添加該項(xiàng)。這兩個(gè)任務(wù) - 如果它存在獲取值,否則創(chuàng)建之 - 可以使用 GetOrCreate() 方法來實(shí)現(xiàn)。修改后的 Show() 方法展示了如何實(shí)現(xiàn)的。
publicIActionResultShow(){stringtimestamp = cache.GetOrCreate? ("timestamp", entry => {returnDateTime.Now.ToString(); });returnView("Show",timestamp);}
Show() 動(dòng)作現(xiàn)在使用 GetOrCreate() 方法。 GetOrCreate() 方法將檢查時(shí)間戳的鍵值是否存在。如果是,現(xiàn)有值將被賦值給局部變量。否則,將根據(jù)第二個(gè)參數(shù)中指定的邏輯創(chuàng)建一個(gè)新條目并將其添加到緩存中。
為了測試此代碼,請直接運(yùn)行 /Home/Show,不需要跳轉(zhuǎn)到 /Home/Index。你仍然會(huì)看到輸出的時(shí)間戳值,因?yàn)樵谠撝挡淮嬖诘那闆r下,GetOrCreate() 現(xiàn)在是添加了它。
7. 你可以在一個(gè)緩存的數(shù)據(jù)項(xiàng)上面設(shè)置絕對和滾動(dòng)的過期時(shí)間
在前述示例中,一個(gè)緩存項(xiàng)只要被添加到緩存就會(huì)一直存儲(chǔ),除非它被明確地使用 Remove() 從緩存中移除。你也可以在一個(gè)緩存項(xiàng)上面設(shè)置一個(gè)絕對和滾動(dòng)的過期時(shí)間。一個(gè)絕對的過期設(shè)置意味著該緩存項(xiàng)會(huì)在嚴(yán)格指定的日期和時(shí)間點(diǎn)被移除,而滾動(dòng)過期設(shè)置則意味著它在給定的一段時(shí)間量處于空閑狀態(tài)(也就是沒人去訪問)之后被移除。
為了能在一個(gè)緩存項(xiàng)上面設(shè)置這兩種過期策略,你要用到 MemoryCacheEntryOptions 對象。如下代碼向你展示了如何去使用。
MemoryCacheEntryOptions options =newMemoryCacheEntryOptions();options.AbsoluteExpiration = DateTime.Now.AddMinutes(1);options.SlidingExpiration = TimeSpan.FromMinutes(1);cache.Set("timestamp", DateTime.Now.ToString(), options);
上述代碼來自于修改過的 Index() action,它創(chuàng)建了一個(gè) MemoryCacheEntryOptions 的對象,然后將它的 AbsoluteExpiration 屬性設(shè)置為從此刻到一分鐘之后的一個(gè) DateTime 值,它還將?SlidingExpiration 屬性設(shè)置為一分鐘。這些值都指定了該緩存項(xiàng)會(huì)在一分鐘之后從緩存移除,不管其是否會(huì)被訪問。此外,如果該緩存項(xiàng)如初持續(xù)空閑了有一分鐘,它也會(huì)被從緩存中移除。
等你將 AbsoluteExpiration 和 SlidingExpiration 的值設(shè)置后, Set() 方法就可以被用來將一個(gè)數(shù)據(jù)項(xiàng)添加到緩存。這一次 MemoryCacheEntryOptions 對象會(huì)被作為第三個(gè)參數(shù)傳遞給 Set() 方法。
8. 當(dāng)緩存項(xiàng)會(huì)被移除時(shí),你可以連接回調(diào)
有時(shí)你會(huì)想要在緩存項(xiàng)從緩存中被移除時(shí)收到通知。可能會(huì)有多種原因需要從緩存中移除數(shù)據(jù)項(xiàng)。例如,因?yàn)槊鞔_地執(zhí)行了?Remove() 方法而移除了一個(gè)緩存項(xiàng), 也有可能是因?yàn)樗?AbsoluteExpiration 和 SlidingExpiration 值已經(jīng)到期而被移除,諸如此類的原因。
為了能知道項(xiàng)目是何時(shí)從緩存移除的,你需要編寫一個(gè)緩存函數(shù)。如下代碼向你展示了如何去做這件事情:
MemoryCacheEntryOptions options =newMemoryCacheEntryOptions();options.AbsoluteExpiration = DateTime.Now.AddMinutes(1);options.SlidingExpiration = TimeSpan.FromMinutes(1);options.RegisterPostEvictionCallback(MyCallback,this);cache.Set("timestamp", DateTime.Now.ToString(), options);
上述代碼同之前使用 MemoryCacheEntryOptions 來配置 AbsoluteExpiration 和 SlidingExpiration 的代碼相當(dāng)類似。更加重要的是它也調(diào)用了 RegisterPostEvictionCallback() 方法來綁定剛剛討論過的回調(diào)函數(shù)。在這里回調(diào)函數(shù)被命名為?MyCallback。第二個(gè)參數(shù)是一個(gè)你會(huì)想要傳遞給回調(diào)函數(shù)的狀態(tài)對象。這里我們傳入了?HomeController 的實(shí)例 (用 this 將當(dāng)前的 HomeController 對象“點(diǎn)”出來) 作為狀態(tài)對象。
前面提到的MyCallback函數(shù),其代碼如下所示:
privatestaticvoidMyCallback(objectkey,objectvalue,EvictionReason reason,objectstate){varmessage = $"Cache entry was removed : {reason}";? ? ((HomeController)state).cache.Set("callbackMessage", message);}
請仔細(xì)觀察這段代碼。 MyCallback()?是 HomeController 類里面的一個(gè)私有靜態(tài)函數(shù),它有四個(gè)參數(shù)。前面兩個(gè)參數(shù)表示剛剛刪除的緩存項(xiàng)的鍵和值,第三個(gè)參數(shù)表示的是該數(shù)據(jù)項(xiàng)被刪除的原因。EvictionReason 是一個(gè)枚舉類型,它維護(hù)者各種可能的刪除原因,如過期,刪除以及替換。
在回調(diào)函數(shù)的內(nèi)部,我們會(huì)基于刪除的原因構(gòu)造一個(gè)字符串消息。我們想要將此消息設(shè)置成另外一個(gè)緩存項(xiàng)。這樣做的話就需要訪問 HomeController 的緩存對象,此時(shí)狀態(tài)參數(shù)就可以排上用場了。使用狀態(tài)對象,你可以對 HomeController 的緩存對象進(jìn)行控制,并使用 Set()?增加一個(gè) callbackMessage 緩存項(xiàng)。
你可以通過 Show()?這個(gè) action 來訪問到 callbackMessage,如下所示:
public IActionResultShow(){stringtimestamp= cache.Get("timestamp");? ViewData["callbackMessage"] =? ? cache.Get("callbackMessage");? return View("Show",timestamp);
}
最后就可以在 Show 視圖中顯示出來了:
TimeStamp : @Model
@ViewData["callbackMessage"]
@Html.ActionLink("Go back","Index","Home")
歡迎關(guān)注我的公眾號(hào)(同步更新文章):DoNet技術(shù)分享平臺(tái)
閱讀原文
總結(jié)
以上是生活随笔為你收集整理的关于 ASP.NET 内存缓存你需要知道的 10 点的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Vue之过渡动画
- 下一篇: Visual Studio 2017 的