如何使用 System.Runtime.Caching.MemoryCache 简单地缓存对象






4.80/5 (27投票s)
本文提供了一种简化使用 System.Runtime.Caching.MemoryCache 来缓存应用程序对象的方法
引言
本文展示了如何通过统一继承的 Singleton 结构来正确使用 System.Runtime.Caching.MemoryCache。
背景
应用程序缓存对象非常常见,无论是服务器端应用程序(如 WCF 服务 (每次调用)),Web 应用程序(服务器端),甚至是简单的独立 Windows 应用程序(WPF/Win-Forms)。
.NET 4.0 提供了一个非常简单的缓存对象,名为 System.Runtime.Caching.MemoryCache。
但是,我们如何正确使用这个对象呢?
根据我的经验,最好将这些对象统一并简化为一个结构化的实用程序,以便在应用程序中使用,原因如下:
- 避免应用程序中出现代码重复
MemoryCache
配置统一,易于更改- 团队中经验不足的程序员可以更轻松地使用该对象
缓存提供程序(基础)
让我们使用一个包含以下内容的 abstract
基类来包装 System.Runtime.Caching.MemoryCache:
MemoryCache
实例- 锁定机制
- 错误日志
public abstract class CachingProviderBase
{
public CachingProviderBase()
{
DeleteLog();
}
protected MemoryCache cache = new MemoryCache("CachingProvider");
static readonly object padlock = new object();
protected virtual void AddItem(string key, object value)
{
lock (padlock)
{
cache.Add(key, value, DateTimeOffset.MaxValue);
}
}
protected virtual void RemoveItem(string key)
{
lock (padlock)
{
cache.Remove(key);
}
}
protected virtual object GetItem(string key, bool remove)
{
lock (padlock)
{
var res = cache[key];
if (res != null)
{
if (remove == true)
cache.Remove(key);
}
else
{
WriteToLog("CachingProvider-GetItem: Don't contains key: " + key);
}
return res;
}
}
#region Error Logs
string LogPath = System.Environment.GetEnvironmentVariable("TEMP");
protected void DeleteLog()
{
System.IO.File.Delete(string.Format("{0}\\CachingProvider_Errors.txt", LogPath));
}
protected void WriteToLog(string text)
{
using (System.IO.TextWriter tw = System.IO.File.AppendText(string.Format("{0}\\CachingProvider_Errors.txt", LogPath)))
{
tw.WriteLine(text);
tw.Close();
}
}
#endregion
}
全局缓存提供程序
接下来,我将创建一个全局应用程序缓存的示例,该缓存用于常见缓存活动,例如简单地缓存对象、获取和删除对象。
public interface IGlobalCachingProvider
{
void AddItem(string key, object value);
object GetItem(string key);
}
全局缓存提供程序继承自 CachingProviderBase
,并公开一个接口化的单例。
public class GlobalCachingProvider : CachingProviderBase, IGlobalCachingProvider
{
#region Singleton
protected GlobalCachingProvider()
{
}
public static GlobalCachingProvider Instance
{
get
{
return Nested.instance;
}
}
class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly GlobalCachingProvider instance = new GlobalCachingProvider();
}
#endregion
#region ICachingProvider
public virtual new void AddItem(string key, object value)
{
base.AddItem(key, value);
}
public virtual object GetItem(string key)
{
return base.GetItem(key, true);//Remove default is true because it's Global Cache!
}
public virtual new object GetItem(string key, bool remove)
{
return base.GetItem(key, remove);
}
#endregion
}
Using the Code
为了简单起见,我将通过一个基本的 WPF 应用程序演示 System.Runtime.Caching.MemoryCache 的使用。
该应用程序正在缓存一个文本消息,并且仅在创建演示对象时才获取该消息。演示对象与存储消息的对象完全断开连接,甚至在存储消息时不必存在。
该过程如下:
后台代码如下:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
pesenterCombobox.Items.Add("Message-Box");
pesenterCombobox.Items.Add("Text-Block");
pesenterCombobox.Items.Add("List-Box");
pesenterCombobox.SelectedIndex = 0;
}
private void StoreBtn_Click(object sender, RoutedEventArgs e)
{
string text = new TextRange(inputTextBox.Document.ContentStart,
inputTextBox.Document.ContentEnd).Text;
//Store the message in the cache:
GlobalCachingProvider.Instance.AddItem("Message", text);
}
private void PresentBtn_Click(object sender, RoutedEventArgs e)
{
//fetch the message from the cache:
var message = GlobalCachingProvider.Instance.GetItem("Message") as string;
switch (pesenterCombobox.SelectedIndex)
{
//"Message-Box"
case 0:
{
MessageBox.Show(message);
break;
}
//"Text-Block"
case 1:
{
contentPresenter.Content = new TextBlock() { Text = message };
break;
}
//"List-Box"
case 2:
{
if (message != null)
{
var listbox = new ListBox();
var lines = message.Split('\n');
foreach (var ln in lines)
listbox.Items.Add(new ListViewItem() { Content = ln, Height = 16 });
contentPresenter.Content = listbox;
}
break;
}
}
}
}
进一步讨论
本文的目的是提供一种使用应用程序实用程序来统一和简化代码的方法。在规划应用程序时,我们应该模块化我们的代码,以实现:
- 未来更改的灵活性
- 易于维护
模块化代码是统一的、简单的且易于更改的,它也很容易维护,因为每个模块都是系统的一小部分。 无需在 1,000 行代码(LOC)中搜索错误,只需检查〜100 LOC。
模块化应用程序应经常使用强大的、统一的和简化的实用程序。 例如:
- ThreadInvoker - 使我们能够简化和更改线程的使用。
LoggerManager
- 使我们能够简化和更改日志记录器的使用。DataAccessManager
- 使我们能够简化和更改对数据的访问:有时可能是对Database
的访问,有时是 WCF 服务,或者两者兼而有之...... 并且我们可以在不触及业务逻辑代码的情况下更改它!- 等等。
历史
- 2014年5月11日 - 添加“进一步讨论”部分