演练:在 WPF 应用程序中缓存应用程序数据
缓存可以将数据存储在内存中以便快速访问。 再次访问数据时,应用程序可以从缓存获取数据,而不是从原始源检索数据。 这可改善性能和可伸缩性。 此外,数据源暂时不可用时,缓存可提供数据。
.NET Framework 提供了使你能够在 .NET Framework 应用程序中使用缓存的类。 这些类位于 System.Runtime.Caching 命名空间中。
注意
System.Runtime.Caching 是 .NET Framework 4 中的新命名空间。 此命名空间使所有 .NET Framework 应用程序都可以使用缓存。 旧版 .NET Framework 仅在 System.Web 命名空间中提供缓存,因此,需要 ASP.NET 类上的一个依赖项。
本演练介绍如何使用 .NET Framework 中提供的缓存功能作为 Windows Presentation Foundation (WPF) 应用程序的一部分。 在本演练中,你将缓存文本文件的内容。
本演练演示以下任务:
创建 WPF 应用程序项目。
添加对 .NET Framework 4 的引用。
初始化缓存。
添加包含文本文件内容的缓存条目。
为缓存条目提供逐出策略。
监视缓存文件的路径,并就受监视项的变化通知缓存实例。
先决条件
若要完成本演练,你将需要:
Visual Studio 2010。
包含少量文本的文本文件。 (将在消息框中显示文本文件的内容。)本演练中演示的代码假定你正在使用以下文件:
c:\cache\cacheText.txt
不过,你可以使用任何文本文件并对本演练中的代码稍做更改。
创建 WPF 应用程序项目
首先创建一个 WPF 应用程序项目。
创建 WPF 应用程序
启动 Visual Studio。
在“文件”菜单中,单击“新建”,然后单击“新建项目”。
随即显示“新建项目”对话框。
在“已安装的模板”下,选择要使用的编程语言(Visual Basic 或 Visual C#)。
在“新建项目”对话框中,选择“WPF 应用程序”。
注意
如果未显示“WPF 应用程序”模板,请确保以支持 WPF 的 .NET Framework 版本为目标。 在“新建项目”对话框中,从列表中选择 .NET Framework 4。
在“名称”文本框中,输入项目的名称。 例如,可以输入 WPFCaching。
选择“为解决方案创建目录”复选框。
单击“确定”。
WPF 设计器会在“设计”视图中打开并显示 MainWindow.xaml 文件。 Visual Studio 会创建“我的项目”文件夹、Application.xaml 文件和 MainWindow.xaml 文件。
以 .NET Framework 为目标并添加对缓存程序集的引用
默认情况下,WPF 应用程序以 .NET Framework 4 客户端配置文件为目标。 若要在 WPF 应用程序中使用 System.Runtime.Caching 命名空间,应用程序必须以 .NET Framework 4(而不是 .NET Framework 4 客户端配置文件)为目标,并且必须包含对该命名空间的引用。
因此,下一步是更改 .NET Framework 目标并添加对 System.Runtime.Caching 命名空间的引用。
注意
更改 .NET Framework 目标的过程在 Visual Basic 项目和 Visual C# 项目中有所不同。
在 Visual Basic 中更改目标 .NET Framework
在解决方案资源管理器中,右键单击项目名称,然后单击“属性”。
随即显示应用程序的属性窗口。
单击“编译” 选项卡。
在窗口底部,单击“高级编译选项…”。
随即显示“高级编译器设置”对话框。
在“目标框架(所有配置)”列表中,选择“.NET Framework 4”。 (请勿选择“.NET Framework 4 客户端配置文件”。)
单击“确定”。
随即显示“目标框架更改”对话框。
在“目标框架更改”对话框中,单击“是”。
项目将关闭,然后重新打开。
按照以下步骤添加对缓存程序集的引用:
在解决方案资源管理器中,右键单击项目名称,然后单击“添加引用”。
选择“.NET”选项卡,选择
System.Runtime.Caching
,然后单击“确定”。
在 Visual C# 项目中更改目标 .NET Framework
在解决方案资源管理器中,右键单击项目名称,然后单击“属性”。
随即显示应用程序的属性窗口。
单击“应用程序” 选项卡。
在“目标框架”列表中,选择“.NET Framework 4”。 (请勿选择“.NET Framework 4 客户端配置文件”。)
按照以下步骤添加对缓存程序集的引用:
右键单击“引用”文件夹,然后单击“添加引用”。
选择“.NET”选项卡,选择
System.Runtime.Caching
,然后单击“确定”。
向 WPF 窗口添加按钮
接下来,添加按钮控件并为按钮的 Click
事件创建事件处理程序。 稍后将添加代码,以便在单击按钮时,缓存并显示文本文件的内容。
添加按钮控件
在解决方案资源管理器中,双击 MainWindow.xaml 文件将其打开。
从工具箱的“常用 WPF 控件”下,将
Button
控件拖到MainWindow
窗口中。在“属性”窗口中,将
Button
控件的Content
设置为“获取缓存”。
初始化缓存并缓存条目
接下来,添加代码以执行以下任务:
创建缓存类实例,即实例化新的 MemoryCache 对象。
指定缓存使用 HostFileChangeMonitor 对象来监视文本文件中的更改。
读取文本文件,并将其内容缓存为缓存条目。
显示缓存文本文件的内容。
创建缓存对象
双击刚刚添加的按钮,以便在 MainWindow.xaml.cs 或 MainWindow.Xaml.vb 文件中创建事件处理程序。
在文件顶部(类声明之前),添加以下
Imports
(Visual Basic) 或using
(C#) 语句:using System.Runtime.Caching; using System.IO;
Imports System.Runtime.Caching Imports System.IO
在事件处理程序中,添加以下代码以实例化缓存对象:
ObjectCache cache = MemoryCache.Default;
Dim cache As ObjectCache = MemoryCache.Default
ObjectCache 类是提供内存中对象缓存的内置类。
添加以下代码以读取名为
filecontents
的缓存条目的内容:Dim fileContents As String = TryCast(cache("filecontents"), String)
string fileContents = cache["filecontents"] as string;
添加以下代码以检查名为
filecontents
的缓存条目是否存在:If fileContents Is Nothing Then End If
if (fileContents == null) { }
如果指定的缓存条目不存在,则必须读取文本文件并将其作为缓存条目添加到缓存中。
在
if/then
块中,添加以下代码以创建一个新的 CacheItemPolicy 对象,该对象指定缓存条目在 10 秒后过期。Dim policy As New CacheItemPolicy() policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10.0)
CacheItemPolicy policy = new CacheItemPolicy(); policy.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(10.0);
如果未提供逐出或过期信息,则默认为 InfiniteAbsoluteExpiration,这意味着缓存条目永远不会仅基于绝对时间过期。 相反,缓存条目仅在存在内存压力时才会过期。 作为最佳做法,应始终显式提供绝对过期或可调过期。
在
if/then
块内上一步添加的代码后面,添加以下代码以创建要监视的文件路径集合,并将文本文件的路径添加到集合中:Dim filePaths As New List(Of String)() filePaths.Add("c:\cache\cacheText.txt")
List<string> filePaths = new List<string>(); filePaths.Add("c:\\cache\\cacheText.txt");
注意
如果要使用的文本文件不是
c:\cache\cacheText.txt
,请指定要使用的文本文件的路径。在上一步添加的代码后面,添加以下代码以将新的 HostFileChangeMonitor 对象添加到缓存条目的更改监视器集合中:
policy.ChangeMonitors.Add(New HostFileChangeMonitor(filePaths))
policy.ChangeMonitors.Add(new HostFileChangeMonitor(filePaths));
HostFileChangeMonitor 对象监视文本文件的路径,并在发生更改时通知缓存。 在此示例中,如果文件内容发生更改,缓存条目将过期。
在上一步添加的代码后面,添加以下代码以读取文本文件的内容:
fileContents = File.ReadAllText("c:\cache\cacheText.txt") & vbCrLf & DateTime.Now.ToString()
fileContents = File.ReadAllText("c:\\cache\\cacheText.txt") + "\n" + DateTime.Now;
系统添加了日期和时间时间戳,以便你能够看到缓存条目何时过期。
在上一步添加的代码后面,添加以下代码以将文件的内容作为 CacheItem 实例插入缓存对象:
cache.Set("filecontents", fileContents, policy)
cache.Set("filecontents", fileContents, policy);
可通过将之前创建的 CacheItemPolicy 对象作为参数传递,指定有关应如何逐出缓存条目的信息。
在
if/then
块后面添加以下代码,以在消息框中显示缓存的文件内容:MessageBox.Show(fileContents)
MessageBox.Show(fileContents);
在“生成”菜单中,单击“生成 WPFCaching”以生成项目。
在 WPF 应用程序中测试缓存
现在可以测试此应用程序。
在 WPF 应用程序中测试缓存
按 Ctrl+F5 运行应用程序。
随即显示
MainWindow
窗口。单击“获取缓存”。
来自文本文件的缓存内容显示在消息框中。 请注意文件上的时间戳。
关闭消息框,然后再次单击“获取缓存”。
时间戳不变。 这表明显示的是缓存内容。
等待 10 秒或更长时间,然后再次单击“获取缓存”。
这次显示一个新的时间戳。 这表明该策略让缓存条目过期并显示新的缓存内容。
在文本编辑器中,打开创建的文本文件。 暂时不要进行任何更改。
关闭消息框,然后再次单击“获取缓存”。
再次注意时间戳。
对文本文件进行更改,然后保存文件。
关闭消息框,然后再次单击“获取缓存”。
此消息框包含来自文本文件的更新内容和新时间戳。 这表明主机文件更改监视器在你更改文件时立即逐出缓存条目,即使绝对超时期限尚未到期也是如此。
注意
你可以将逐出时间增加到 20 秒或更长时间,以便有更多时间对文件进行更改。
代码示例
完成本演练后,你创建的项目的代码将类似于以下示例。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.Caching;
using System.IO;
namespace WPFCaching
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
ObjectCache cache = MemoryCache.Default;
string fileContents = cache["filecontents"] as string;
if (fileContents == null)
{
CacheItemPolicy policy = new CacheItemPolicy();
policy.AbsoluteExpiration =
DateTimeOffset.Now.AddSeconds(10.0);
List<string> filePaths = new List<string>();
filePaths.Add("c:\\cache\\cacheText.txt");
policy.ChangeMonitors.Add(new
HostFileChangeMonitor(filePaths));
// Fetch the file contents.
fileContents = File.ReadAllText("c:\\cache\\cacheText.txt") + "\n" + DateTime.Now.ToString();
cache.Set("filecontents", fileContents, policy);
}
MessageBox.Show(fileContents);
}
}
}
Imports System.Runtime.Caching
Imports System.IO
Class MainWindow
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Dim cache As ObjectCache = MemoryCache.Default
Dim fileContents As String = TryCast(cache("filecontents"), _
String)
If fileContents Is Nothing Then
Dim policy As New CacheItemPolicy()
policy.AbsoluteExpiration = _
DateTimeOffset.Now.AddSeconds(10.0)
Dim filePaths As New List(Of String)()
filePaths.Add("c:\cache\cacheText.txt")
policy.ChangeMonitors.Add(New _
HostFileChangeMonitor(filePaths))
' Fetch the file contents.
fileContents = File.ReadAllText("c:\cache\cacheText.txt") & vbCrLf & DateTime.Now.ToString()
cache.Set("filecontents", fileContents, policy)
End If
MessageBox.Show(fileContents)
End Sub
End Class