在实际开发过程中,缓存(Cache)是一项重要技术。有时候为了缓解数据库访问的压力,我们可以将一些需要经常读取但又几乎不会变化的数据存在缓存里,以此加快数据的访问速度。在ASP.NET Core中,缓存一般分为本地缓存和分布式缓存。相较于分布式缓存(Redis),本地缓存并不会将数据写入磁盘中,它只是将数据存储在内存中进行操作,因此本地缓存的数据会随着应用程序的重启而丢失。一般情况下,如果需要存储的数据不多,同时也没有数据持久化的要求,则可以考虑使用本地缓存。下面开始介绍其使用方法。
打开Startup.cs文件,添加本地缓存的相关服务,代码如下所示:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace App
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// 启用本地缓存
services.AddMemoryCache();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
在ASP.NET Core中,我们只需要在Controller中注入IMemoryCache接口即可对缓存进行操作,代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
}
}
TryGetValue可以根据key来判断某个缓存是否存在,代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 判断缓存是否存在
///
///
[HttpGet]
public ActionResult<string> Get()
{
if (cache.TryGetValue("UserName", out _))
{
return "该缓存存在";
}
else
{
return "该缓存不存在";
}
}
}
}
Get和Set主要负责读取和写入缓存,代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 读写缓存
///
///
[HttpGet]
public ActionResult<string> Get()
{
// 写入缓存
cache.Set("UserName", "admin");
cache.Set("Password", "12345");
// 读取缓存
string userName = cache.Get<string>("UserName");
string password = cache.Get<string>("Password");
// 返回
return userName + "\n" + password;
}
}
}
GetOrCreate可以实现:如果一个缓存值存在,则返回它,如果不存在,则创建该缓存后再返回它。代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 读写缓存
///
///
[HttpGet]
public ActionResult<string> Get()
{
string userName = cache.GetOrCreate("UserName", entry =>
{
return "admin";
});
string password = cache.GetOrCreate("Password", entry =>
{
return "12345";
});
return userName + "\n" + password;
}
}
}
Remove可以实现缓存的删除。代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 删除缓存
///
///
[HttpGet]
public ActionResult<string> Get()
{
// 写入缓存
cache.Set("UserName", "admin");
cache.Set("Password", "12345");
// 删除缓存
cache.Remove("UserName");
cache.Remove("Password");
// 返回
return "缓存删除成功";
}
}
}
一般情况下,缓存都是通过时间来判断是否过期的。常用的4种时间过期策略如下所示:
永不过期是指在整个应用程序生命周期内,缓存不会过期。代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System.Threading;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 永不过期
///
///
[HttpGet]
public ActionResult<string> Get()
{
string msg = string.Empty;
cache.Set("UserName", "admin");
// 读取缓存
for (int i = 1; i <= 5; i++)
{
msg += $"第{i}秒缓存值:{cache.Get<string>("UserName")}\n";
Thread.Sleep(1000);
}
// 返回
return msg;
}
}
}
程序运行结果如下图所示:

绝对时间过期是指:缓存只有在规定时间内才能被命中,超过该规定时间则删除该缓存。代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Threading;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 绝对时间过期
///
///
[HttpGet]
public ActionResult<string> Get()
{
string msg = string.Empty;
cache.Set("UserName", "admin", TimeSpan.FromSeconds(3));
// 读取缓存
for (int i = 1; i <= 5; i++)
{
msg += $"第{i}秒缓存值:{cache.Get<string>("UserName")}\n";
Thread.Sleep(1000);
}
// 返回
return msg;
}
}
}
上面代码设置的绝对过期时间为3秒,因此第1秒到第3秒能够获取到缓存值,而第4秒和第5秒则无法获取到缓存值,运行结果如下图所示:

滑动时间过期是指:在规定时间内,如果缓存被命中,则过期时间往后顺延。如果在规定时间内该缓存一直未被命中,则删除该缓存。因此,如果一个滑动过期的缓存在其过期时间内一直被访问,则该缓存永远不会过期。代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Threading;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 滑动时间过期
///
///
[HttpGet]
public ActionResult<string> Get()
{
string msg = string.Empty;
cache.Set("UserName", "admin", new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromSeconds(3)
});
// 读取缓存
for (int i = 1; i <= 5; i++)
{
msg += $"第{i}秒缓存值:{cache.Get<string>("UserName")}\n";
Thread.Sleep(1000);
}
// 返回
return msg;
}
}
}
在上面的代码中,滑动过期时间为3秒,因此每次访问缓存后都会使其过期时间往后顺延3秒,运行结果如下图所示:

如果将滑动过期时间改为0.5秒,则第1次命中该缓存后,过期时间往后顺延0.5秒,但下一次访问是在1秒后,此时缓存已经过期,因此只有第1次能获取到该缓存值,运行结果如下图所示:

绝对时间过期+滑动时间过期很好理解:任意一个策略失效都会导致另一个策略失效。代码如下所示:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Threading;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 绝对时间过期+滑动时间过期
///
///
[HttpGet]
public ActionResult<string> Get()
{
string msg = string.Empty;
cache.Set("UserName", "admin", new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromSeconds(1.5),
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(3)
});
// 读取缓存
for (int i = 1; i <= 5; i++)
{
msg += $"第{i}秒缓存值:{cache.Get<string>("UserName")}\n";
Thread.Sleep(1000);
}
// 返回
return msg;
}
}
}
上面的代码将绝对过期时间设置为3秒,滑动过期时间设置为1.5秒,因此每次命中缓存后,其过期时间都会往后顺延1.5秒,但由于绝对过期时间不能超过3秒,因此第1秒到第3秒可以获取到缓存,第4秒到第5秒则无妨访问到缓存。运行结果如下图所示:

如果将绝对过期时间设置为3秒,滑动过期时间设置为0.5秒,在第一次命中缓存后,其过期时间往后顺延0.5秒,但第二次访问时滑动时间策略已经失效,所以绝对时间过期策略也会失效,因此只有第一次能访问到该缓存,运行结果如下图所示:

缓存被删除一般有两种情况:手动删除和缓存过期。在部分特殊情况下,我们需要在某个缓存被删除后执行一些其他任务,这时候就涉及到删除缓存后的回调函数,即:RegisterPostEvictionCallback。先来看一个例子:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Threading;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public HomeController(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 回调函数
///
///
[HttpGet]
public ActionResult<string> Get()
{
MemoryCacheEntryOptions options = new MemoryCacheEntryOptions
{
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(1)
};
options.RegisterPostEvictionCallback((key, value, reason, state) =>
{
Console.WriteLine($"回调函数输出【键:{key},值:{value},被清除的原因:{reason}】");
});
// 删除缓存
cache.Set("UserName", "admin", options);
cache.Remove("UserName");
// 缓存过期
cache.Set("UserName", "admin", options);
Thread.Sleep(2000);
return cache.Get<string>("UserName");
}
}
}
在上面的代码中,我们先创建了一个缓存然后将其删除,接着又创建了一个缓存然后等待其过期。这两种情况都会触发RegisterPostEvictionCallback。运行结果如下图所示:

定义IMemoryCacheHelper接口,代码如下:
using System.Collections.Generic;
namespace App
{
public interface IMemoryCacheHelper
{
///
/// 获取缓存
///
/// 缓存值类型
/// 键
/// 缓存值
TValue Get<TValue>(string key);
///
/// 获取缓存
///
/// 缓存值类型
/// 键集合
/// 缓存值集合
List<TValue> Get<TValue>(List<string> keys);
///
/// 设置缓存
///
/// 缓存值类型
/// 键
/// 值
/// 过期时间
/// 是否滑动过期
/// 是否成功
bool Set<TValue>(string key, TValue value, int expires = 0, bool isSliding = false);
///
/// 判断缓存是否存在
///
/// 键
/// 是否存在
bool IsExist(string key);
///
/// 移除缓存
///
/// 键
/// 是否成功
bool Remove(string key);
///
/// 移除缓存
///
/// 键集合
/// 是否成功
bool Remove(List<string> keys);
}
}
定义MemoryCacheHelper类实现IMemoryCacheHelper接口,代码如下:
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
namespace App
{
public class MemoryCacheHelper : IMemoryCacheHelper
{
private readonly IMemoryCache cache;
///
/// 构造函数
///
///
public MemoryCacheHelper(IMemoryCache cache)
{
this.cache = cache;
}
///
/// 获取缓存
///
/// 缓存值类型
/// 键
/// 缓存值
public TValue Get<TValue>(string key)
{
return cache.Get<TValue>(key);
}
///
/// 获取缓存
///
/// 缓存值类型
/// 键集合
/// 缓存值集合
public List<TValue> Get<TValue>(List<string> keys)
{
List<TValue> list = new List<TValue>();
foreach (string key in keys)
{
list.Add(cache.Get<TValue>(key));
}
return list;
}
///
/// 设置缓存
///
/// 缓存值类型
/// 键
/// 值
/// 过期时间
/// 是否滑动过期
/// 是否成功
public bool Set<TValue>(string key, TValue value, int expires = 0, bool isSliding = false)
{
try
{
if (expires == 0)
{
cache.Set(key, value);
}
else
{
if (isSliding)
{
cache.Set(key, value, new MemoryCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromSeconds(expires)
});
}
else
{
cache.Set(key, value, new MemoryCacheEntryOptions
{
AbsoluteExpiration = DateTime.Now.AddSeconds(expires)
});
}
}
return true;
}
catch
{
return false;
}
}
///
/// 判断缓存是否存在
///
/// 键
/// 是否存在
public bool IsExist(string key)
{
if (string.IsNullOrEmpty(key) || string.IsNullOrWhiteSpace(key))
{
return false;
}
else
{
return cache.TryGetValue(key, out _);
}
}
///
/// 移除缓存
///
/// 键
/// 是否成功
public bool Remove(string key)
{
try
{
cache.Remove(key);
return true;
}
catch
{
return false;
}
}
///
/// 移除缓存
///
/// 键集合
/// 是否成功
public bool Remove(List<string> keys)
{
try
{
foreach (string key in keys)
{
cache.Remove(key);
}
return true;
}
catch
{
return false;
}
}
}
}
在Startup.cs中注入IMemoryCacheHelper接口,代码如下:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace App
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMemoryCache();
services.AddScoped<IMemoryCacheHelper, MemoryCacheHelper>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
最后在HomeController的构造函数中注入IMemoryCacheHelper即可,代码如下:
using Microsoft.AspNetCore.Mvc;
namespace App.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class HomeController : ControllerBase
{
private readonly IMemoryCacheHelper helper;
///
/// 构造函数
///
///
public HomeController(IMemoryCacheHelper helper)
{
this.helper = helper;
}
///
/// 创建缓存
///
///
[HttpGet]
public ActionResult<string> Create()
{
helper.Set<string>("UserName", "admin");
helper.Set<string>("Password", "12345", 3, false);
return helper.Get<string>("UserName") + "\n" + helper.Get<string>("Password");
}
///
/// 移除缓存
///
///
[HttpGet]
public ActionResult<string> Remove()
{
if (helper.IsExist("UserName"))
{
helper.Remove("UserName");
}
if (helper.IsExist("Password"))
{
helper.Remove("Password");
}
return "缓存删除成功";
}
}
}
本文主要介绍了ASP.NET Core中本地缓存MemoryCache的使用方法,熟练掌握缓存的各种过期策略可以使你在实际应用中更加得心应手地使用缓存。