易于使用的 CacheManager






2.57/5 (6投票s)
2002年3月14日
4分钟阅读

83120

745
一个管理ASP.NET数据缓存的.NET类
引言
ASP.NET提供了一个System.Web.Caching.Cache
类。每个ASP.NET应用程序域都有此类的一个实例,可以通过Page.Cache
属性访问。缓存用作临时的内存存储。与ASPApplication
变量一样,Cache
是应用程序级别的内存存储,它存储应用程序中所有用户会话共有的数据。缓存还执行自己的线程管理。您不必在更改其中存储的数据之前锁定它,之后再解锁它,就像您必须对Application
那样做。
缓存的另一个特性是您可以设置其过期时间。缓存项可以在绝对时间点过期(被删除),也可以使用滑动时间窗口,或者在删除另一个缓存项时失效,或者文件更改时失效,或者目录中的任何文件更改时失效。
使用Cache
很简单。基本上,您只需要使用Insert
来存储数据,并使用Get
来访问数据。但是您必须确定数据是否已存在于缓存中,这反过来又要求您有一个构建密钥的方案。因此您仍然需要做一些工作。CacheManager
用于保护您免受管理缓存密钥的细节的影响。
CacheManager
在我们的场景中,我们必须从数据源获取数据。它可能是SQL服务器或互联网上的Web服务。要获取的数据量很大,并且源数据变化不频繁。因此,我们不想过于频繁地获取它。数据是确定性的,这意味着给定的输入始终提供给定的数据集,除非数据源中的数据已更改,这在大多数现实生活应用程序中都应该是这种情况。
我们可以使用所有输入来组合密钥,并将获取的数据作为值。在大多数情况下,用户身份的任何部分都不是查询数据源的输入的一部分。这意味着缓存项可以在具有相同输入的所有用户会话之间共享。
我们还想在DataGrid
中显示数据。由于可能返回许多数据行,因此我们希望在DataGrid
中使用分页。每次页面更改时,都必须重新绑定DataGrid
。如果数据未缓存,则必须再次从数据源获取数据。在许多具有大型数据集的情况下,组合DataGrid
分页和服务器数据缓存是解决ASP.NET性能问题的唯一解决方案。当然,您可以设计一个自定义分页解决方案,该解决方案依赖于仅返回特定页面数据的数据库。但它可能不会表现更好,并且需要付出更多努力才能实现。
CacheManager
类只有这些公共成员
public delegate object SourceDataDelegate(object[] parameters);
public event System.EventHandler PreGetFromSource;
public event System.EventHandler PostGetFromSource;
public object GetCachedData(string cacheCategory, int expireSeconds,
SourceDataDelegate getSourceData, object[] parameters)
GetCachedData
的参数
cacheCategory
:区分应用程序不同部分中使用的不同数据。
expireSeconds
:缓存项的过期时间。
getSourceData
:当缓存中找不到数据时,CacheManager
回调的委托。
parameters
:回调委托使用的参数。它也用于与cacheCategory
一起组合缓存键。
这是一个代码片段,显示如何使用CacheManager
private void ShowData()
{
DataTable table = GetDataFromCache();
if (table != null)
{
DataGrid1.DataSource = table.DefaultView ;
}
DataGrid1.DataBind();
}
private DataTable GetDataFromCache()
{
string placeName = SearchText.Text;
CacheManager cm = new CacheManager();
cm.PostGetFromSource += new System.EventHandler(this.GotFromSource);
object[] ar = new object[] {placeName};
CacheManager.SourceDataDelegate dlg = +
new CacheManager.SourceDataDelegate(this.GetDataFromWebService);
DataTable dt = (DataTable) cm.GetCachedData("Place", 60, dlg, ar);
return dt;
}
private object GetDataFromWebService(object[] parameters)
{
string placeName = parameters[0] as string;
Geonet.Location[] places;
try
{
Geonet.PlaceFinder finder = new Geonet.PlaceFinder();
Geonet.LocationInfo locinfo = finder.findPlace(placeName);
places = locinfo.candidates;
if (places == null)
{
ErrorMessage.Text = "No place found with this name ";
return null;
}
}
catch (System.Net.WebException e)
{
ErrorMessage.Text = "Service not available at present ";
return null;
}
catch
{
ErrorMessage.Text = "An error occured ";
return null;
}
DataTable placeTable = new DataTable("places");
placeTable.Columns.Add( new DataColumn("Name"));
placeTable.Columns.Add( new DataColumn("Place"));
placeTable.Columns.Add( new DataColumn("Type") );
for (int i = 0; i<places.Length; i++)
{
string[] place = {
places[i].description2,
places[i].description1,
places[i].type };
placeTable.Rows.Add(place);
}
return placeTable;
}
我们连接到geographynetwork.com webservice以获取以我们提供的文本开头的地名列表,从列表中构建一个DataTable
并将其存储在缓存中。然后我们将DataTable
绑定到DataGrid
以在Web窗体中显示。非常简单,不是吗?
考虑因素
缓存数据会消耗内存。通过仔细设置过期时间,您可以平衡存储在缓存中的数据量及其获取频率。应考虑正常的用户行为。他们在点击下一页之前会在DataGrid
页面停留多长时间?
应尽可能避免将重叠的数据集存储在不同的缓存项中。例如,如果用户查询欧洲的所有客户,然后他或另一个用户查询法国的所有客户。缓存中将存在重叠数据。我们的缓存管理器不够智能,无法弄清楚这一点。较短的过期时间是防止高内存使用的保障措施。我们还可以选择另一种策略。当用户查询法国的所有客户时,我们可以查询欧洲的所有客户,但Web窗体仅使用数据的子集(法国客户)。使用此策略,过期时间足够长是有利的。有几种方法可以实现此策略,我将其留给读者。
如果您想讨论或评论本文中的任何内容,欢迎通过电子邮件与我联系(zhong.yu@telia.com)。