65.9K
CodeProject 正在变化。 阅读更多。
Home

易于使用的 CacheManager

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.57/5 (6投票s)

2002年3月14日

4分钟阅读

viewsIcon

83120

downloadIcon

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)。

© . All rights reserved.