原文:http://www.binaryintellect.net/articles/a7d9edfd-1f86-45f8-a668-64cc86d8e248.aspx
环境:Visual Studio 2017, Asp.Net Core 1.1.

缓存机制主要是为了提高性能。在ASP.NET Web Forms 及 ASP.NET MVC中可以直接使用缓存对象(Caching object)缓存数据。这通常被称作“服务器端缓存”,是 Framework 的内置特性。虽然Asp.Net Core没有这样的缓存对象,但依然可以轻松的实现缓存功能。本文将对此进行介绍。

继续阅读之前,请先使用 Web Application 项目模板创建一个 Asp.net Core 的应用程序:

注:使用缓存功能,需通过NuGet安装“Microsoft.Extensions.Caching.Memory”扩展包。使用时引用:using Microsoft.Extensions.Caching.Memory;

1. 在Startup类中开启缓存功能

如前所述,Asp.Net Core 没有内建的缓存对象,因此不能在 Controller 中使用它们。在Asp.Net Core中要使用内存缓存,需要在 Startup 类中依赖注入(DI)内存缓存服务(in-memory caching service)。打开Startup 文件,修改 ConfigureServices() 方法如下:

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddMemoryCache();
}

AddMemoryCache() 方法将内存缓存功能加入到服务集合中。

2. 控制器中注入缓存对象

修改HomeController如下:

public class HomeController : Controller
{
private IMemoryCache cache; public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
...
}

通过构造函数注入缓存对象,我们在控制器中就有了可用的缓存变量。

3. 使用Set()方法缓存一个对象

有了IMemoryCache 对象,你就可以非常方便读取或写入缓存数据。

public IActionResult Index()
{
cache.Set<string>("timestamp", DateTime.Now.ToString());
return View();
}

上面代码使用 IMemoryCache 的 Set<T>() 方法将一条记录加入到了缓存中。Set() 方法第一个参数为缓存名称,第二个参数为其值。

4. 使用Get()方法获取缓存

一旦您添加了缓存,就可以通过 Get() 方法来获取它们。

public IActionResult Show()
{
string timestamp = cache.Get<string>("timestamp");
return View("Show", timestamp);
}

上面代码演示了从缓存中获得数据,Get() 方法指定了缓存名和返回数据类型。

下面用于显示获得的缓存:

<h1>TimeStamp : @Model</h1>
<h2>@Html.ActionLink("Go back", "Index", "Home")</h2>

运行你的应用程序,打开 /Home/Index ,此时将生成了一个时间戳并存入缓存中。然后打开 /Home/Show 观察是否已从缓存中获得了时间戳数据。/Home/Show 页面会显示如下:

5. 使用TryGet()方法检查是否存在指定缓存项

上面示列中,每次访问 /Home/Index 都将生成一个新的时间戳并进行缓存,这是因为没有检查是否已存在同名的缓存项。有两种方式检测同名缓存项:

//first way
if (string.IsNullOrEmpty(cache.Get<string>("timestamp")))
{
cache.Set<string>("timestamp", DateTime.Now.ToString());
} //second way
if (!cache.TryGetValue<string>("timestamp", out string timestamp))
{
cache.Set<string>("timestamp", DateTime.Now.ToString());
}

第一种方法仍然使用Get()方法,如未能获取到值,则返回 IsNullOrEmpty()。

第二种方法更加优雅一些,使用TryGet()方法获取值。TryGet()方法返回一个布尔值来表示缓存项是否存在,实际的缓存值将通过 out 参数获得。

6. 使用GetOrCreate()添加不存在的缓存项

你可能有这样的需求,缓存项如果存在则取出,如果不存在则创建。此时可以使用 GetOrCreate() 方法:

public IActionResult Show()
{
string timestamp = cache.GetOrCreate<string>("timestamp", entry => {
return DateTime.Now.ToString();
});
return View("Show", timestamp);
}

使用GetOrCreate() 方法检查timestamp是否存在,如果存在则直接赋值给本地变量,反之则创建一个新的缓存项。

现在直接访问/Home/Show页面,可以看到timestamp已显示。timestamp并没有通过/Home/Index赋值。

7. 设置缓存的决绝和滑动(sliding)期限

在之前的示列中,缓存数据会一直存在,直到调用Remove()方法。你也可以为缓存设置绝对期限和滑动期限。绝对期限就是为缓存设置一个明确的过期时间,滑动期限就是空闲多少时间之后移除缓存。

你可以通过 MemoryCacheEntryOptions对象设置缓存期限策略:

public IActionResult Index()
{
//cache.Set<string>("timestamp", DateTime.Now.ToString()); MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.AbsoluteExpiration = DateTime.Now.AddMinutes();
options.SlidingExpiration = TimeSpan.FromMinutes();
cache.Set<string>("timestamp", DateTime.Now.ToString(), options); return View();
}

上面代码在Index() action中创建了MemoryCacheEntryOptions对象,并同时设置了一个绝对期限为1分钟与滑动期限为1分钟的策略。MemoryCacheEntryOptions对象作为Set()方法的第三个参数传递。

8. 定义移除缓存之后的回调函数

有时候你可能需要在缓存移除后获得通知。缓存被移除有以下2种原因,一是调用了Remove()方法,二是到期后自动移除。你可以通过设定回调函数获得移除缓存之后的通知:

public IActionResult Index()
{
//cache.Set<string>("timestamp", DateTime.Now.ToString()); MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.AbsoluteExpiration = DateTime.Now.AddMinutes();
options.SlidingExpiration = TimeSpan.FromMinutes();
options.RegisterPostEvictionCallback(MyCallback, this);
cache.Set<string>("timestamp", DateTime.Now.ToString(), options); return View();

上面代码示例了通过RegisterPostEvictionCallback方法设定回调函数,在本例中回调函数名为MyCallback,第二个参数是你希望传递给回调函数的状态参数,本例我们将HomeController实例作为状态参数传递给回调函数。

回调函数方法如下:

private static void MyCallback(object key, object value, EvictionReason reason, object state)
{
var message = $"Cache entry was removed : {reason}";
((HomeController)state).cache.Set("callbackMessage", message);
}

看看上面代码,MyCallback() 是一个在HomeController中的私有静态函数,有四个参数。前两个参数是被移除的缓存数据所对应的键/值,第三个参数表示移除的原因。EvictionReason是一个枚举类型,可能原因有过期、移除和替换。

在函数体中,我们仅定义了一个string类型的message表示移除原因。如果我们想要把这个message作为另一个缓存项,就需要访问HomeController的缓存对象,此时就要使用第四个参数state。使用state对象获得HomeController中的缓存,同时使用Set()方法设置了一个名为“callbackMessage”的缓存项。

callbackMessage可以在Show() action中获得:

public IActionResult Show()
{
string timestamp = cache.Get<string>("timestamp");
ViewData["callbackMessage"] = cache.Get<string>("callbackMessage");
return View("Show", timestamp);
}

在Show页面呈现:

<h1>TimeStamp : @Model</h1>
<h3>@ViewData["callbackMessage"]</h3>
<h2>@Html.ActionLink("Go back", "Index", "Home")</h2>

测试回调方法,运行程序并访问/Home/Index页面,然后再访问/Home/Show页面,不断刷新Show页面直到缓存过期,页面会显示过期原因。

9. 缓存的优先级

和过期策略一样,缓存优先级同样使用MemoryCacheEntryOptions:

MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.Priority = CacheItemPriority.Normal;
cache.Set<string>("timestamp", DateTime.Now.ToString(), options);

CacheItemPriority枚举值有普通,高和永久保留。

10. 设置缓存之间的依赖关系

可以为缓存数据设置依赖关系,实现删除一个缓存项后,其依赖的缓存项同时被移除的功能。请按如下代码更新Index() action:

public IActionResult Index()
{
var cts = new CancellationTokenSource();
cache.Set("cts", cts); MemoryCacheEntryOptions options = new MemoryCacheEntryOptions();
options.AddExpirationToken(new CancellationChangeToken(cts.Token));
options.RegisterPostEvictionCallback(MyCallback, this);
cache.Set<string>("timestamp",DateTime.Now.ToString(), options); cache.Set<string>("key1", "Hello World!",new CancellationChangeToken(cts.Token));
cache.Set<string>("key2", "Hello Universe!",new CancellationChangeToken(cts.Token)); return View();
}
注:通过NuGet安装“Microsoft.Extensions.Primitives”程序包,同时添加对System.Threading的引用。

上面代码首先创建了一个CancellationTokenSource对象,并将其设置成名为“cts”的缓存项。然后创建MemoryCacheEntryOptions对象,并使用AddExpirationToken()方法指定一个特殊的过期令牌(expiration token),这里我们暂不深究CancellationChangeToken。

expiration token允许你“过期”一个缓存项,如果令牌是“active”状态则保留缓存项,如果是“cancelled”则从缓存中移除。一旦缓存项被移除则调用MyCallback方法。代码接着创建了两个缓存项 key1和key2,创建时第三参数传递了一个使用cts对象初始化的CancellationChangeToken。

现在我们有了三个缓存项,timestamp是主缓存项,key1和key2依赖与timestamp。当timestamp被移除时,会同时移除key1和key2。移除timestamp需要取消它的令牌。

public IActionResult Remove()
{
CancellationTokenSource cts = cache.Get<CancellationTokenSource>("cts");
cts.Cancel();
return RedirectToAction("Show");
}

我们从缓存中获得先前定义的CancellationTokenSource对象同时执行Cancel()方法,timestamp移除的同时key1与key2也一并移除了。

测试一下上面代码,运行程序并访问/Home/Index页面,再访问/Home/Show页面,同时检查三个缓存项是否按预期显示。然后访问/Home/Remove,你将重新被导航到/Home/Show页面。当Remove() action被调用时,令牌被标记为取消,所有的值将被移除。Show页面将显示过期原因为“令牌过期”:

Asp.Net Core 缓存的使用(译)的更多相关文章

  1. ASP.NET Core 缓存技术 及 Nginx 缓存配置

    前言 在Asp.Net Core Nginx部署一文中,主要是讲述的如何利用Nginx来实现应用程序的部署,使用Nginx来部署主要有两大好处,第一是利用Nginx的负载均衡功能,第二是使用Nginx ...

  2. Asp.net Core 缓存 MemoryCache 和 Redis

    Asp.net Core 缓存 MemoryCache 和 Redis 目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 经过 N 久反复的尝试,翻阅了网上无数的资料,GitH ...

  3. Redis 入门与 ASP.NET Core 缓存

    目录 基础 Redis 库 连接 Redis 能用 redis 干啥 Redis 数据库存储 字符串 订阅发布 RedisValue ASP.NET Core 缓存与分布式缓存 内存中的缓存 ASP. ...

  4. 【无私分享:ASP.NET CORE 项目实战(第十一章)】Asp.net Core 缓存 MemoryCache 和 Redis

    目录索引 [无私分享:ASP.NET CORE 项目实战]目录索引 简介 经过 N 久反复的尝试,翻阅了网上无数的资料,GitHub上下载了十几个源码参考, Memory 和 Redis 终于写出一个 ...

  5. ASP.NET Core缓存静态资源

    背景 缓存样式表,JavaScript或图像文件等静态资源可以提高您网站的性能.在客户端,总是从缓存中加载一个静态文件,这样可以减少对服务器的请求数量,从而减少获取页面及其资源的时间.在服务器端,由于 ...

  6. asp.net core 缓存和Session

    缓存 缓存在内存中 ASP.NET Core 使用 IMemoryCache内存中缓存是使用依赖关系注入从应用中引用的服务. 请在ConfigureServices中调用AddMemoryCache( ...

  7. ASP.NET Core 中间件之压缩、缓存

    前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...

  8. ASP.NET Core 开发-缓存(Caching)

    ASP.NET Core 缓存Caching,.NET Core 中为我们提供了Caching 的组件. 目前Caching 组件提供了三种存储方式. Memory Redis SqlServer 学 ...

  9. [译]ASP.NET Core 2.0 系列文章目录

    基础篇 [译]ASP.NET Core 2.0 中间件 [译]ASP.NET Core 2.0 带初始参数的中间件 [译]ASP.NET Core 2.0 依赖注入 [译]ASP.NET Core 2 ...

随机推荐

  1. C++学习笔记之——内联函数,引用

    本文为原创作品,转载请注明出处 欢迎关注我的博客:http://blog.csdn.net/hit2015spring和http://www.cnblogs.com/xujianqing/ 作者:晨凫 ...

  2. [Leetcode Week8]Subsets

    Subsets 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/subsets/description/ Description Given a set ...

  3. twilio打电话和发短信

    # -*- coding: utf-8 -*- # @Time : 2018/03/09 14:53 # @Author : cxa # @File : call.py # @Software: Py ...

  4. 最简单的DLL

    静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了.但是若使用 DLL,该 DLL 不必被包含在最终 EXE ...

  5. 微信网页版的onclick事件不起作用

    我的错误是在跳转的url中拼接了url,如下: var myBaseUrl="https://xxx/"; function do() { $.ajax({ url :myBase ...

  6. 6.shell判断语句

    [ condition ](注意condition前后要有空格),可以使用$?验证(0为true,>1为false) 两个整数的比较:=:字符串比较-lt:小于-gt:大于-le:小于等于-ge ...

  7. [ 总结 ] Linux kickstart 无人值守安装系统构建过程

    环境:Vmare + Linux虚拟机 注意:网卡桥接

  8. 《Java编程思想》笔记 第八章 多态

    1.向上转型 把子类引用当作父类引用.(子类对象赋值给父类引用) 2.绑定 确定方法属于哪个类. 3.前期绑定 程序执行前绑定. 4.后期绑定也叫动态绑定 程序运行时绑定. 5.多态 多个不同的对象对 ...

  9. Qt笔记——绘图(QBitmap,QPixmap,QImage,QPicture)

    QPainter绘图 重写绘图事件,虚函数 如果窗口绘图,必须放在绘图事件里实现 绘图事件内部自动调用,窗口需要重绘的时候,状态改变 绘图设备(QPixmap,QImage,QBitmap,QPict ...

  10. maven 通过 profile 设置多环境打包

    maven 在设计之初就考虑到了业务代码和测试代码的分开存放.将业务代码默认存放在  src/main  下,将测试代码放在  src/test  下,然后在各自目录下再细分  java  与 res ...