ASP.NET 运行时缓存 - 克隆对象以保留缓存数据






4.57/5 (5投票s)
一篇关于如果要在.NET缓存中保留缓存数据,对象克隆的重要性的文章
引言
让我们想象一下,您已经在内存中填充了一个对象,然后将该对象写入ASP.NET运行时缓存。您知道,当您从缓存中读取您的对象时...
MyClass myObject = ( MyClass )HttpRuntime.Cache[ "mykey" ];
...您将拥有对缓存可以访问的同一对象实例的引用?这意味着您在对象(图2)中所做的任何更改都将被未来对同一缓存对象的所有请求看到。
// no so... I'm afraid every future request will also see this description
myObject.Description = "My very own description";
现在,这不一定是件好事。事实上,这对您来说可能非常糟糕,因为也许,您想缓存一个对象,然后保留该对象直到它从缓存中过期。与此同时,对这个缓存对象的新请求可能希望对其进行小的修改以供自己使用,因此,如果我们需要保留缓存但允许请求修改 - 我们必须为每个请求提供其自己的对象副本。
那么,我们如何保护ASP.NET运行时缓存中的数据?
答案:通过以两种方式克隆对象(在插入缓存和从缓存读取时),使用深拷贝,并且让我强调“深层”。
让我向您展示我为这篇文章创建的一些代码示例。我创建了一个通用的CacheList
类,该类将允许用户向列表中添加通用类型并提供缓存和从缓存加载。我首先创建了一个辅助类,并添加了一个方法,该方法将允许我克隆一个列表。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
public static class GenericsHelper
{
public static IList<T> CloneList<T>( IList<T> source ) where T : ICloneable
{
IList<T> clone = new List<T>( source.Count );
foreach ( T t in source )
{
clone.Add( ( T )t.Clone( ) );
}
return clone;
}
}
然后我实现了一个简单的通用列表,它将允许我
-
// call to add an item to our list Add(T item) : void
-
// call to insert list into the runtime cache CacheIt(string cacheKey, DateTime absoluteExpiration) : void
-
// call to load list from the data in the cache // and allow mods without affecting cache LoadCached(string cacheKey) : bool The generic type must implement ICloneable
我对列表进行了约束,指定我们只能存储实现ICloneable
接口的类型。让我指出,我假设此接口的实现正在执行深拷贝,而不是浅拷贝。
使用搜索引擎搜索ICloneable
,您无疑会找到各种关于它好坏的讨论,老实说,我可以看到反对使用此接口的论点。简而言之,开发人员建议这种方法不够清晰; 它没有指出实现它的对象是执行深拷贝还是浅拷贝。但是,如果您同意反对使用它的原因,那么您实际上没有任何问题;只是您自己编写的cloneable接口……也许您可以使其通用。
/// <summary>
/// A simple class to demonstrate how we can protect/preserve our data
/// in the cache.
/// </summary>
/// <typeparam name="T">Our generic type</typeparam>
public sealed class CacheList<T> where T : ICloneable
{
private IList<T> _dataList = new List<T>( );
public void Add( T item )
{
_dataList.Add( item );
}
public void CacheIt( string cacheKey, DateTime absoluteExpiration )
{
//
// clone the data list
IList<T> clone = GenericsHelper.CloneList<T>( _dataList );
//
// insert into the runtime cache
HttpRuntime.Cache.Insert( cacheKey, clone, null,
absoluteExpiration, Cache.NoSlidingExpiration );
}
public bool LoadCached( string cacheKey )
{
//
// perform thread-safe read from cache
object obj = HttpRuntime.Cache[ cacheKey ];
//
// if this obj is not null then clone it,
// returning true to indicate clone success
if ( obj != null && obj is IList<T> )
{
_dataList = GenericsHelper.CloneList<T>( ( IList<T> )obj );
return true;
}
return false;
}
public IList<T> DataList
{
get { return _dataList; }
}
}
好吧,这就是使用克隆来保留我们缓存数据的代码。这是一个非常简单的例子,也是一个非常简化的列表实现。但是,我不想偏离这篇文章的目的。我将在未来扩展这篇文章,并向您展示这种缓存访问如何被封装在业务/域对象中。
我希望您喜欢这篇文章,还会有更多。我将在我的博客dotnet notepad上撰写更多关于C#、ASP.NET和设计的文章。
有用链接
历史
- 2009年2月13日:初始帖子