C#中的内存缓存
缓存是用来将数据从慢速内存存储到快速内存中,以提高网站中数据的便捷性。在应用程序的开发过程中,应用程序的性能是很重要的。
我们的应用在应用开发过程中运行良好,并不意味着当成千上万的人开始使用它时它会运行良好。需要关注的一个主要领域是数据访问。
经常访问我们的数据库可能是非常昂贵的,特别是当数据没有变化时。由于这个原因,数据缓存被应用于我们的应用程序中,以减少从数据库中直接访问的时间。因此,我们提高了我们的应用程序的性能。
在这篇文章中,我们将讨论.NET框架中的一种缓存方法,即C#中的内存缓存。我们将创建一个没有缓存的应用程序,观察其性能,然后引入缓存,观察性能是如何提高的。
前提条件
要跟上本教程,读者应具备以下条件。
- 对[.NET框架]有基本了解。
- 安装了[Microsoft Visual Studio]。
- 对[C#]编程语言有基本了解。
- 配置了[.NET 5.0]的Microsoft Visual Studio。
构建一个blazor网络应用程序
为了了解内存缓存的工作原理,我们需要创建一个blazor应用程序。让我们首先打开Microsoft Visual Studio,选择create new project ,如下图所示。

在点击create new project ,在下一个屏幕上选择Blazor Server App ,然后点击Next ,如下图所示。

在下一个屏幕上,我们需要输入我们的应用程序的名称,输入名称并点击Next 。对于这个项目,我们将把我们的应用程序命名为CachingApp ,如下图所示。

在下一个屏幕上,我们需要为我们的应用程序选择目标框架,选择.Net Core 5.0(Current) ,然后点击create ,如下图所示。

不使用缓存访问数据
我们将从创建一个新的类库开始。要做到这一点,在解决方案资源管理器窗口中,右键单击Solution ,并单击Add 。然后选择New project ,搜索Class library ,并点击Next 。

在下一个屏幕上,我们将给它命名为ClassLibrary 。使用设置的目标框架并点击Create 。
在创建的类中,我们将删除Class1.cs ,并创建一个名为UserModel 的新类。要做到这一点,右击ClassLibrary ,并选择Add 。在下一个屏幕上选择Class ,输入类的名称为UserModel ,然后点击Add 。
我们将使这个类成为公共的,因此它可以被应用程序中的其他函数访问。这个类将保存用户的信息,也就是名字和姓氏。
我们将在这个类中使用下面的代码片断。
namespace ClassLibrary
{
public class UserModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
我们将创建另一个类,它将模拟与我们的数据库对话的想法。要做到这一点,右击ClassLibrary ,并选择Add 。选择Class ,在下一个屏幕上,输入类的名称为UserModel ,然后点击Add 。
在这个类中,我们将创建一个用户模型的列表来获取用户,调用它的输出,并创建一个new 的实例。这个类返回传递的数据的输出。
现在我们有了一个雇员的列表。但是在现实生活中,我们不会访问两三个文件,我们会访问成千上万的文件。由于这个原因,我们将模拟四秒的延迟,以显示它的运行速度很慢。
namespace ClassLibrary
{
public class SampleClassLibrary
{
public List<UserModel> GetUsers()
{
List<UserModel> output = new();
output.Add(new() { FirstName = "James", LastName = "Courtney" });
output.Add(new() { FirstName = "Caren", LastName = "Hesley" });
output.Add(new() { FirstName = "Emanuel", LastName = "Washington" });
output.Add(new() { FirstName = "John", LastName = "Doe" });
output.Add(new() { FirstName = "Barrack", LastName = "Courtney" });
Thread.Sleep(4000);
return output;
}
}
}
接下来,在Solution Explorer 窗口中右键点击依赖关系,点击Add Project Reference... ,在下一个屏幕上检查ClassLibrary ,然后点击Ok 。
在我们的program.cs 文件中,我们将添加对上述代码的引用。我们将添加样本类库服务,如下面的代码所示。
builder.Services.AddTransient<SampleClassLibrary>();
有了我们的数据类库,我们现在可以在FetchData.razor 文件下的页面文件夹中获取这些数据。我们将删除该文件中的代码,并添加下面代码片段中的代码。
@inject WeatherForecastService ForecastService
@inject SampleClassLibrary data
<h1>User Data</h1>
@if(users is not null)
{
foreach(var e in users)
{
<h3>@e.FirstName @e.LastName</h3>
}
}
@code {
List <UserModel> users;
protected override void onInitialized()
{
users = data.GetUsers();
}
}
在上面的代码片断中,我们注入了SampleClassLibrary ,并给它取名为data 。然后我们调用这个data ,并用雇员的对象对其进行初始化。在 "h3 "标签中的@ ,因为我们混合了HTML 和C# 的代码。
在调试我们的应用程序之前,要做的最后一件事是将我们的类导入到imports.razor 文件的shared 文件夹中。
@using ClassLibrary;
在这个阶段,我们现在可以调试我们的应用程序,观察它在没有缓存的情况下的速度。
引入缓存
现在,让我们在我们的应用程序中引入缓存。这将有助于提高我们应用程序的性能。
我们将在SampleClassLibrary 中使数据访问成为异步的。我们在这个类中添加下面的代码片段来做到这一点。
public async Task <List<UserModel>> GetUsers()
{
List<UserModel> output = new();
output.Add(new() { FirstName = "James", LastName = "Courtney" });
output.Add(new() { FirstName = "Caren", LastName = "Hesley" });
output.Add(new() { FirstName = "Emanuel", LastName = "Washington" });
output.Add(new() { FirstName = "John", LastName = "Doe" });
output.Add(new() { FirstName = "Barrack", LastName = "Courtney" });
await Task.Delay(4000);
return output;
我们使用await Task.Delay(3000); ,而不是Thread.Sleep(400) ,将我们的数据访问时间延迟4秒。现在我们有了一个异步调用,我们可以去FetchData.razor 文件并使用下面的代码片段。
@inject WeatherForecastService ForecastService
@inject SampleClassLibrary data
<h1>User Data</h1>
@if(users is not null)
{
foreach(var e in users)
{
<h3>@e.FirstName @e.LastName</h3>
}
}
@code {
List <UserModel> users;
protected override async Task onInitialized()
{
users = await data.GetUsersCache();
}
}
缓存背后的主要想法是使数据访问异步化。
添加内存中的缓存
为了添加内存缓存,我们将首先进入Program.cs 文件,并添加以下一行代码。为了确保我们的服务是有效的,因为我们的类不知道内存缓存,我们将右键点击ClassLibrary 类。然后我们选择Manage Nuget Packages... ,搜索Microsoft.Extensions.Caching.Memory ,并点击install 来下载它。
builder.Services.AddMemoryCache();
这段代码创建了一个单子,这样我们就可以在我们所有不同的实例中调用它。这意味着我们不应该在这里输入数据,如果我们的最终用户不被允许访问这些数据,他们就会有机会访问。
我们现在将进入我们的SampleClassLibrary 文件,并创建一个新的方法,以获得缓存的用户列表。要做到这一点,我们将使用下面的代码片段。
public async Task <List<UserModel>>GetUserCache()
{
List <UserModel> output;
output = _memoryCache.Get<List<UserModel>>("users");
if(output is null)
{
output = new();
output.Add(new() { FirstName = "James", LastName = "Courtney" });
output.Add(new() { FirstName = "Caren", LastName = "Hesley" });
output.Add(new() { FirstName = "Emanuel", LastName = "Washington" });
output.Add(new() { FirstName = "John", LastName = "Doe" });
output.Add(new() { FirstName = "Barrack", LastName = "Courtney" });
await Task.Delay(4000);
_memoryCache.Set("users", output, Timespan.FromMinutes(2));
}
return output;
}
在上面的代码中,我们正在访问缓存内存中的数据。当第一次访问数据时,if 语句访问数据库中的数据,而缓存内存的输出返回null。
我们还将数据库中的数据存储在名为users 的高速缓冲存储器中,时间只有两分钟。两分钟后,应用程序中的数据被从数据库中访问。
我们的输出有一个未知的实例,因此返回null。然而,我们需要抓取我们的用户列表的缓存版本,所以我们创建了一个SampleClassLibrary 的构造函数并传递了IMemoryCache 的依赖注入。
private readonly IMemoryCache _memoryCache;
public SampleClassLibrary(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
总结
在这篇文章中,我们已经了解到内存缓存是如何增强快速数据访问的。但是,有几件事需要注意,这就是。
- 你应该知道如何正确使用内存缓存--如果你把太多的数据放入缓存内存,你的内存就会耗尽。
- 缓存内存没有根据其访问数据的千字节数进行限制的想法--你可以通过限制在某一时刻存储在内存中的对象的数量来进行限制。
- 当我们把缓存内存用于那些对内存不太吃力的数据时,会使我们的缓存内存变得更轻。
- 我们应该经常为不在高速缓冲存储器中的新数据做准备。