ASP.NET 2.0数据教程之六十:在程序启动阶段缓存数据(转载)

“预装载”(Proactive loading)可以使用2种模式来预装载 数据。第一种模式,Proactive loading使用一些方法( process)来判断源数据 (underlying data)是否发生改变,并及时对缓存数据进行更新——比 如,周期性的检查源数据;或者当源数据发生改变时,立即通知更新。不过该模 式的弊端在于执行起来比较困难,你必须创建、管理、执行一个具体的方法来检 查源数据的更改情况,以更新缓存数据。

另一个模式,同时也是本文要探 讨的内容,就是在程序启动时便装载数据入内存。该模式对缓存静态数据(static data)尤其有用,比如查找数据库表里的记录。

注意:关于“应激装 载”(reactive loading)和“预装载”(proactive loading)的 区别,请参考文章《 Caching Architecture Guide for .NET Framework Applications》的《Managing the Contents of a Cache》章节: (http://msdn2.microsoft.com/en-us/library/ms978503.aspx)

第一步: 在程序启动阶段决定缓存哪些数据

我们在前面2章探讨的reactive loading模式的示例适合处理这些数据:周期性地改变且生成(generate)数据不需 要太长的时间。但是,如果缓存的数据从未改变,那么reactive loading模式使 用的周期(expiry)就显的有点多余。另外,如果需要缓存的数据要花很长的时间 才能生产,当用户请求发现内存为空时,用户将等很长的时间来检索并返回数据 。对此,可以考虑将静态数据和需要很长时间才能生成的数据在程序启动阶段就 缓存。

虽然,数据库有很多动态的,经常改变的值;不过静态值也不少。 举例,数据库表Patients有一个PrimaryLanguage列,其值可以为English, Spanish, French, Russian, Japanese等。不过我们不会直接在表Patients里存 储“English”或 “French”等字符串,而是在供查找的 表Languages里存储。如图1:John Doe的primary language是English,而Ed Johnson的是Russian.

图1:表Languages为表Patients所使用的查找表

在编辑或创建 新patient的用户界面里,将包含一个下拉列表框,列出表Languages里的所有语 言项。不缓存的话,每次登录该界面,系统都会查询表Languages,这样显地和浪 费也没有必要。因为表Languages不会频繁的改变。

我们可以用前面探讨 的reactive loading模式来对数据Languages进行缓存。不过,reactive loading 模式会使用基于时间的缓存周期(time-based expiry),这对静态数据来说没有必 要。最好的办法是在程序启动阶段进行预装载。

在本文,我们将探讨如何 缓存“查找表”(lookup table,例如Languages表对Patients表来说就 是查找表)数据和其它的静态信息。

第二步:考察缓存数据的不同途径

在一个ASP.NET应用程序里,我们可以使用多种方法来缓存信息。在前面 的教程我们看到的是data cache,其实通过使用static members(静态成员)或 application state(应用程序状态)我们也可以将对象(objects)缓存。

当 处理一个类时,我们在访问其成员(members)前,应先实例化。比如,为了调用 BLL层里的一个方法,我们首先要创建该类的实例:

ProductsBLL productsAPI = new ProductsBLL();
productsAPI.SomeMethod();
productsAPI.SomeProperty = "Hello, World!";

在 调用SomeMethod或处理SomeProperty之前,我们必须首先用关键字new来创建一个 类的实例。SomeMethod 和 SomeProperty要与一个具体的实例对应起来,这些成 员的生命周期(lifetime)取决与对应对象的生命周期。另一方面,Static members,比如变量、属性、方法等,对该类的所有实例来说都是共享的,因此其 生命周期与该类的生命周期一样长。Static members要用关键字static来标识。

除了static members外,还可以使用application state。每一个ASP.NET 应用程序都包含一个name/value集,它对应用程序的所有页面和用户都是共享的 。可以通过HttpContext class类的Application property属性来访问它。在页面 的后台代码我们可以这样访问它:

Application["key"] = value;
object value = Application ["key"];

data cache提供了丰富的缓存数据的API(应 用程序接口),基于时间和从属体的缓存周期(time- and dependency-based expiries)的机制,以及cache item priorities等。在本文,我们将看到3种缓存 静态数据的技术。

第三步:缓存Suppliers Table表的数据

我们用 到的Northwind数据库并没有“查找表”(lookup tables),DAL层用到 的4个表的值也并非静态的。没必要花时间来向DAL层添加一个新数据库表,再在 BLL层添加新的类和新的方法,我们在本教程假定表Suppliers的数据是静态的, 因此我们在程序启动是缓存其数据。

首先,我们在CL文件夹里创建一个名 为StaticCache.cs的新类。

图2:在CL文件夹里创建StaticCache.cs类

我们需要添加一个在 程序启动时装载数据的方法;同样,还有一个从内存返回数据的方法。

[System.ComponentModel.DataObject]
public class StaticCache
{
  private static Northwind.SuppliersDataTable suppliers = null;

  public static void LoadStaticCache() 
  {
    // Get suppliers - cache using a static member variable
    SuppliersBLL suppliersBLL = new SuppliersBLL ();
    suppliers = suppliersBLL.GetSuppliers();
  }
  [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
  public static Northwind.SuppliersDataTable GetSuppliers()
   {
    return suppliers;
  }
}

在上 述代码里,我们在LoadStaticCache()方法里,用一个static member变量 suppliers来保存SuppliersBLL类的GetSuppliers()方法返回的结果。该 LoadStaticCache()方法应该在程序启动阶段就被调用。一旦数据在启动时就被加 载到内存,任何要用到supplier信息的页面都可以调用StaticCache class类的 GetSuppliers()方法。因此,访问数据库获取suppliers信息的情况只会发生一次 ,就是在启动阶段。

除了static member变量外,我们还可以使用 application state 或data cache。下面的代码将类进行修改,它使用 application state:

[System.ComponentModel.DataObject]
public class StaticCache
{
  public static void LoadStaticCache()
  {
    // Get suppliers - cache using application state
    SuppliersBLL suppliersBLL = new SuppliersBLL();
    HttpContext.Current.Application ["key"] = suppliersBLL.GetSuppliers();
  }

  [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
  public static Northwind.SuppliersDataTable GetSuppliers()
   {
    return HttpContext.Current.Application ["key"] as Northwind.SuppliersDataTable;
  }
}

在LoadStaticCache()方法里,supplier信息是存储在 application变量key里。在GetSuppliers()方法里,它作为 Northwind.SuppliersDataTable类型返回。由于我们可以在ASP.NET页面的后台代 码里使用Application["key"]来访问application state,所以,在这 里我们必须使用HttpContext.Current.Application["key"]来获取当 前的HttpContext。

同样,我们可以使用data cache,如下所示:

[System.ComponentModel.DataObject]
public class StaticCache
{
  public static void LoadStaticCache()
  {
    // Get suppliers - cache using the data cache
    SuppliersBLL suppliersBLL = new SuppliersBLL();
     HttpRuntime.Cache.Insert(
     /* key */         "key",
     /* value */       suppliers,
     /* dependencies */    null,
     /* absoluteExpiration */ Cache.NoAbsoluteExpiration,
     /* slidingExpiration */ Cache.NoSlidingExpiration,
     /* priority */      CacheItemPriority.NotRemovable,
     /* onRemoveCallback */  null);
  }

   [DataObjectMethodAttribute(DataObjectMethodType.Select, true)]
   public static Northwind.SuppliersDataTable GetSuppliers()
   {
    return HttpRuntime.Cache["key"] as Northwind.SuppliersDataTable;
  }
}

向data cache添加一个条目,且没指定时间周期(no time-based expiry)为此,我们 System.Web.Caching.Cache.NoAbsoluteExpiration 和 System.Web.Caching.Cache.NoSlidingExpiration值作为输入参数之一。在上面 的data cache的Insert()方法里,我们指定了缓存条目的优先级(priority).优先 级用以指明当内存容量不足时,哪些条目应从内存移除。在此,我们将优先级设 为不可移除(也就是对应的null),这就确保了当内存不足时不会将其移除。

注意:本文下载代码里的StaticCache class类使用的是 static member 变量技术,关于application state 和 data cache技术的代码可以在类文件 (class file)里的注释部分找到。

第四步:在程序启动是执行代码

为了在程序启动时执行代码,我们需要创建一个名为Global.asax的文件 。该文件包含了application、session和request级事件的事件处理器。在该文件 里我们将添加在程序启动时要执行的代码。

要在网站根目录里添加 Global.asax文件,在Visual Studio解决资源管理器里,右击网站项目,选Add New Item,从Add New Item对话框里选择Global应用程序项目类型,然后点Add按 钮。

注意:如果你的根目录里已经存在Global.asax文件,Global应用程 序项目类型就不会出现在Add New Item对话框里。

图3:在根目录添加Global.asax文件。

默认的Global.asax文件 里包括了5个方法,每个方法都有一个服务器端(server-side)<script>标 记:

Application_Start –当程序启动时执行

Application_End – 当程序完结时执行

Application_Error – 每当程序发生未经处理(unhandled)的异常时发生。

Session_Start – 当创建一个session时执行

Session_End – 当session完结时或被移除时发生

Application_Start事件处理器 在程序的生命周期(life cycle)里只发生一次。程序起始于一个ASP.NET资源 (resource)首次被请求,持续运行直到程序重新启动为止。关于程序生命周期的 更多细节请参阅文章《ASP.NET Application Life Cycle Overview》 http://msdn2.microsoft.com/en-us/library/ms178473.aspx

本文,我们 只需要为Application_Start方法添加代码,放心大胆的将其它方法删除。在 Application_Start里,仅仅调用StaticCacheclass类的LoadStaticCache()方法 。这将装载并缓存supplier信息:

<%@ Application Language="C#" %>
<script runat="server">
  void Application_Start(object sender, EventArgs e)
  {
     StaticCache.LoadStaticCache();
  }
</script>

要做的就是这些!在程序开始时, LoadStaticCache()方法会从BLL获取supplier信息,再存储进一个static member 变量(或是你在StaticCache class类里面用的其它一些cache store)。为验证起 见,在Application_Start 方法里设置断点(breakpoint)并执行程序。另外,在 并发请求(Subsequent requests)时,不会执行Application_Start方法。

图4:用Breakpoint来验证Application_Start事件处理器的执行

注意:如果你在首次调试时没有遇到Application_Start breakpoint,那 是因为你的程序已经启动了。可以修改Global.asax 或 Web.config文件来强迫程 序重新启动。你仅仅在这些文件的末尾添加(或删除)一个空白行来快速的重启程 序。

第五步:展示缓存数据

现在,StaticCache class类在程序启 动时将supplier相关的数据进行了缓存。要在表现层使用这些数据,我们可以在 ASP.NET页面的后台代码通过ObjectDataSource控件或编程调用StaticCache class类的GetSuppliers()方法。让我们看看如何使用ObjectDataSource 和 GridView控件来展示缓存的supplier信息。

首先,打开文件夹里的 AtApplicationStartup.aspx页面,在“设计”模式里从工具箱里拖一 个GridView控件到页面,设置其ID为Suppliers。然后,从其智能标签里选择创建 一个新的ObjectDataSource,名为SuppliersCachedDataSource,设置它使用 StaticCache class类的GetSuppliers()方法。

图5:设置ObjectDataSource控件使用StaticCache Class类

图6:使用GetSuppliers()方法来获取缓存的Supplier数据

完成 设置后,Visual Studio会自动的为SuppliersDataTable里的每一个列添加一个 BoundFields。因此,你的GridView 和 ObjectDataSource控件的声明标记看起来 应该像下面这样:

<asp:GridView ID="Suppliers" runat="server" AutoGenerateColumns="False"
   DataKeyNames="SupplierID" DataSourceID="SuppliersCachedDataSource"
   EnableViewState="False">
  <Columns>
     <asp:BoundField DataField="SupplierID" HeaderText="SupplierID"
       InsertVisible="False" ReadOnly="True"
       SortExpression="SupplierID" />
     <asp:BoundField DataField="CompanyName" HeaderText="CompanyName"
       SortExpression="CompanyName" />
     <asp:BoundField DataField="Address" HeaderText="Address"
       SortExpression="Address" />
     <asp:BoundField DataField="City" HeaderText="City"
       SortExpression="City" />
    <asp:BoundField DataField="Country" HeaderText="Country"
       SortExpression="Country" />
     <asp:BoundField DataField="Phone" HeaderText="Phone"
       SortExpression="Phone" />
  </Columns>
</asp:GridView>

<asp:ObjectDataSource ID="SuppliersCachedDataSource" runat="server"
  OldValuesParameterFormatString="original_{0}"
   SelectMethod="GetSuppliers" TypeName="StaticCache" />

图7显示的是在浏览器登录该页面的画面。同样都是用BLL层 的SuppliersBLL class类来获取数据,不同的是我们用StaticCache class类在程 序开始时将数据缓存并将其返回。你可以在StaticCache class类的 GetSuppliers()方法里设置断点来进行验证。

图7:将缓存的Supplier数据显示在GridView控件

结语:

几乎每一种数据模式(data model)都包含有静态数据,且通常情况下都会 用到对应的"查找表"(lookup tables)。正因为这些信息是静态的,所 以没有必要每次展示数据时都访问数据库。此外,因其“静态”的本 质,当缓存数据时没有必要设置周期(expiry).在本文,我们看到了如何用data cache, application state和static member变量来缓存数据。这些数据在程序启 动是就进行缓存,且贯穿程序的整个生命周期(lifetime)中,都会保留在内存里 。

在本文及前面2章,我们探讨了在程序的生命周期内缓存数据,以及使 用基于时间的缓存周期(time-based expiries)。当缓存数据库数据时,若源数据 (underlying database data)改变时我们应将对应的缓存条目移除。在这个问题 的处理上,虽然使用基于时间的缓存周期的方法还算不上完美,但与通过编程来 “刷新”数据相比,还算上佳方案。最佳方案或许是使用SQL cache dependencies,对此,我们将在接下来的文章继续探讨。

祝编程快乐!

作者简介

Scott Mitchell,著有六本ASP/ASP.NET方面的书,是 4GuysFromRolla.com的创始人,自1998年以来一直应用 微软Web技术。Scott是个 独立的技术咨询顾问,培训师,作家,最近完成了将由Sams出版社出版的新作, 24小时内精通ASP.NET 2.0。他的联系电邮为mitchell@4guysfromrolla.com,也 可以通过他的博客http://ScottOnWriting.NET与他联系。

posted @ 2011-12-17 17:23  ^_^肥仔John  阅读(216)  评论(0编辑  收藏  举报