使用 ObjectDataSource 缓存数据 (C#)

作者 :Scott Mitchell

下载 PDF

缓存可能意味着慢速和快速 Web 应用程序之间的差异。 本教程是四个教程中的第一个,详细介绍了 ASP.NET 中的缓存。 了解缓存的关键概念以及如何通过 ObjectDataSource 控件将缓存应用于表示层。

简介

在计算机科学中, 缓存 是获取成本高昂的数据或信息并将其副本存储在访问速度更快的位置的过程。 对于数据驱动型应用程序,大型复杂查询通常占用应用程序的大部分执行时间。 因此,通过将昂贵的数据库查询结果存储在应用程序的内存中,通常可以提高此类应用程序的性能。

ASP.NET 2.0 提供各种缓存选项。 整个网页或用户控件呈现的标记可以通过 输出缓存来缓存。 ObjectDataSource 和 SqlDataSource 控件还提供缓存功能,从而允许在控件级别缓存数据。 ASP.NET 数据缓存 提供了丰富的缓存 API,使页面开发人员能够以编程方式缓存对象。 在本教程和接下来的三个教程中,我们将检查如何使用 ObjectDataSource 缓存功能以及数据缓存。 我们还将探讨如何在启动时缓存应用程序范围的数据,以及如何通过使用 SQL 缓存依赖项使缓存数据保持最新。 这些教程不探讨输出缓存。 有关输出缓存的详细信息,请参阅 ASP.NET 2.0 中的输出缓存

缓存可以在体系结构中的任何位置应用,从数据访问层到表示层。 在本教程中,我们将了解如何通过 ObjectDataSource 控件将缓存应用于表示层。 在下一教程中,我们将探讨在业务逻辑层缓存数据。

关键缓存概念

缓存可以极大地提高应用程序的整体性能和可伸缩性,方法是获取生成成本高昂的数据,并将数据的副本存储在可以更有效地访问的位置。 由于缓存只保存实际基础数据的副本,因此,如果基础数据发生更改,它可能会过时或 过时。 为了解决此问题,页面开发人员可以使用以下任一方法指示从缓存中 逐出 缓存项的条件:

  • 在绝对或滑动持续时间内,可以向缓存添加项的基于时间的条件。 例如,页面开发人员可能指示持续时间为 60 秒。 在绝对持续时间内,缓存项在添加到缓存后的 60 秒内被逐出,无论访问频率如何。 使用滑动持续时间时,缓存的项在上次访问后 60 秒被逐出。
  • 基于依赖项的条件 ,依赖项在添加到缓存时可以与项相关联。 当项的依赖项发生更改时,会将其从缓存中逐出。 依赖项可以是文件、另一个缓存项或两者的组合。 ASP.NET 2.0 还允许 SQL 缓存依赖项,使开发人员能够将项添加到缓存,并在基础数据库数据更改时将其逐出。 我们将在即将发布的使用 SQL 缓存依赖项教程中检查 SQL 缓存依赖项

无论指定的逐出条件如何,在满足基于时间或基于依赖项的条件之前,缓存中的项可能会被 清理 。 如果缓存已达到其容量,必须先删除现有项,然后才能添加新项。 因此,以编程方式处理缓存数据时,始终假定缓存的数据可能不存在至关重要。 在下一教程“ 在体系结构中缓存数据”中,我们将介绍以编程方式从缓存访问数据时要使用的模式。

缓存提供了一种经济的方法,用于挤压应用程序中的更多性能。 正如 Steven Smith 在他的文章 ASP.NET 缓存:技术和最佳做法中阐明的那样:

缓存是获取足够好性能的好方法,无需花费大量时间和分析。 内存很便宜,因此,如果可以通过缓存输出 30 秒(而不是花费一天或一周时间尝试优化代码或数据库)获得所需的性能,请执行缓存解决方案 (假设 30 秒旧数据可以) ,然后继续操作。 最终,糟糕的设计可能会赶上你,所以你当然应该尝试正确设计应用程序。 但是,如果你现在只需要获得足够好的性能,缓存可能是一种出色的 [方法],这让你有时间在以后有时间重构应用程序。

虽然缓存可以提供明显的性能增强,但它并不适用于所有情况,例如,对于使用实时、频繁更新数据的应用程序,或者即使是生存期较短的过时数据也不可接受的情况。 但对于大多数应用程序,应使用缓存。 有关 ASP.NET 2.0 中缓存的更多背景信息,请参阅 ASP.NET 2.0 快速入门教程缓存性能部分。

步骤 1:创建缓存网页

在开始探索 ObjectDataSource 缓存功能之前,让我们先花点时间在网站项目中创建 ASP.NET 页面,本教程和接下来的三个页面将需要这些页面。 首先添加名为 Caching的新文件夹。 接下来,将以下 ASP.NET 页添加到该文件夹,确保将每个页面与 Site.master 母版页相关联:

  • Default.aspx
  • ObjectDataSource.aspx
  • FromTheArchitecture.aspx
  • AtApplicationStartup.aspx
  • SqlCacheDependencies.aspx

为 Caching-Related 教程添加 ASP.NET 页面

图 1:为 Caching-Related 教程添加 ASP.NET 页面

与其他文件夹中一样, Default.aspx 文件夹中 Caching 会列出其部分中的教程。 回想一下, SectionLevelTutorialListing.ascx 用户控件提供了此功能。 因此,通过将用户控件从解决方案资源管理器拖动到Default.aspx页面设计视图中,将此用户控件添加到 。

图 2:将 SectionLevelTutorialListing.ascx 用户控件添加到 Default.aspx

图 2:图 2:将 SectionLevelTutorialListing.ascx 用户控件添加到 Default.aspx (单击以查看全尺寸图像)

最后,将这些页作为条目添加到文件中 Web.sitemap 。 具体而言,在“使用二进制数据 <siteMapNode>”之后添加以下标记:

<siteMapNode title="Caching" url="~/Caching/Default.aspx" 
    description="Learn how to use the caching features of ASP.NET 2.0.">
    <siteMapNode url="~/Caching/ObjectDataSource.aspx" 
        title="ObjectDataSource Caching" 
        description="Explore how to cache data directly from the 
            ObjectDataSource control." />
    <siteMapNode url="~/Caching/FromTheArchitecture.aspx" 
        title="Caching in the Architecture" 
        description="See how to cache data from within the 
            architecture." />
    <siteMapNode url="~/Caching/AtApplicationStartup.aspx" 
        title="Caching Data at Application Startup" 
        description="Learn how to cache expensive or infrequently-changing 
            queries at the start of the application." />
    <siteMapNode url="~/Caching/SqlCacheDependencies.aspx" 
        title="Using SQL Cache Dependencies" 
        description="Examine how to have data automatically expire from the 
            cache when its underlying database data is modified." />
</siteMapNode>

更新 Web.sitemap后,请花点时间通过浏览器查看教程网站。 左侧菜单现在包含缓存教程的项。

站点地图现在包含缓存教程的条目

图 3:站点地图现在包含缓存教程的条目

步骤 2:在网页中显示产品列表

本教程探讨如何使用 ObjectDataSource 控件的内置缓存功能。 不过,在了解这些功能之前,我们首先需要一个页面。 让我们创建一个网页,该网页使用 GridView 列出 ObjectDataSource 从 类检索到的 ProductsBLL 产品信息。

首先打开 ObjectDataSource.aspx 文件夹中的页面 Caching 。 将 GridView 从“工具箱”拖到Designer上,将其 ID 属性设置为 Products,并从其智能标记中选择将其绑定到名为 ProductsDataSource的新 ObjectDataSource 控件。 将 ObjectDataSource 配置为使用 ProductsBLL 类。

将 ObjectDataSource 配置为使用 ProductsBLL 类

图 4:将 ObjectDataSource 配置为使用 ProductsBLL 类 (单击以查看全尺寸图像)

对于此页面,让我们创建一个可编辑的 GridView,以便我们可以检查通过 GridView 接口修改 ObjectDataSource 中缓存的数据时会发生什么情况。 将 SELECT 选项卡中的下拉列表设置为默认值 , GetProducts()但将“更新”选项卡中 UpdateProduct 的选定项更改为接受 productNameunitPriceproductID 作为其输入参数的重载。

将 UPDATE 选项卡 Drop-Down 列表设置为相应的 UpdateProduct 重载

图 5:将 UPDATE 选项卡 Drop-Down 列表设置为适当的 UpdateProduct 重载 (单击以查看全尺寸图像)

最后,将“插入”和“删除”选项卡中的下拉列表设置为 (“无”) 然后单击“完成”。 完成“配置数据源”向导后,Visual Studio 将 ObjectDataSource 属性 OldValuesParameterFormatString 设置为 original_{0}。 如 插入、更新和删除数据概述 教程中所述,需要从声明性语法中删除此属性或将其设置回默认值 {0},以便更新工作流继续而不出错。

此外,在向导完成时,Visual Studio 会为每个产品数据字段向 GridView 添加一个字段。 删除除 、 CategoryNameUnitPrice BoundField 之外ProductName的所有字段。 接下来,将 HeaderText 每个 BoundFields 的属性分别更新为 Product、Category 和 Price。 ProductName由于 字段是必需的,因此请将 BoundField 转换为 TemplateField,并将 RequiredFieldValidator 添加到 。EditItemTemplate 同样,将 UnitPrice BoundField 转换为 TemplateField 并添加 CompareValidator,以确保用户输入的值是大于或等于零的有效货币值。 除了这些修改之外,还可以随意执行任何美观更改,例如右对齐 UnitPrice 值,或在只读和编辑界面中指定文本的格式 UnitPrice

通过选中 GridView 智能标记中的“启用编辑”复选框,使 GridView 可编辑。 另检查“启用分页”和“启用排序”复选框。

注意

需要查看如何自定义 GridView 编辑界面? 如果是这样,请参阅 自定义数据修改接口 教程。

启用 GridView 对编辑、排序和分页的支持

图 6:启用 GridView 对编辑、排序和分页的支持 (单击以查看全尺寸图像)

进行这些 GridView 修改后,GridView 和 ObjectDataSource 的声明性标记应如下所示:

<asp:GridView ID="Products" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
    AllowPaging="True" AllowSorting="True">
    <Columns>
        <asp:CommandField ShowEditButton="True" />
        <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
            <EditItemTemplate>
                <asp:TextBox ID="ProductName" runat="server" 
                    Text='<%# Bind("ProductName") %>'></asp:TextBox>
                <asp:RequiredFieldValidator 
                    ID="RequiredFieldValidator1" Display="Dynamic" 
                    ControlToValidate="ProductName" SetFocusOnError="True"
                    ErrorMessage="You must provide a name for the product."
                    runat="server">*</asp:RequiredFieldValidator>
            </EditItemTemplate>
            <ItemTemplate>
                <asp:Label ID="Label2" runat="server" 
                    Text='<%# Bind("ProductName") %>'></asp:Label>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            ReadOnly="True" SortExpression="CategoryName" />
        <asp:TemplateField HeaderText="Price" SortExpression="UnitPrice">
            <EditItemTemplate>
                $<asp:TextBox ID="UnitPrice" runat="server" Columns="8" 
                    Text='<%# Bind("UnitPrice", "{0:N2}") %>'></asp:TextBox>
                <asp:CompareValidator ID="CompareValidator1"
                    ControlToValidate="UnitPrice" Display="Dynamic" 
                    ErrorMessage="You must enter a valid currency value with no 
                        currency symbols. Also, the value must be greater than 
                        or equal to zero."
                    Operator="GreaterThanEqual" SetFocusOnError="True" 
                    Type="Currency" runat="server" 
                    ValueToCompare="0">*</asp:CompareValidator>
            </EditItemTemplate>
            <ItemStyle HorizontalAlign="Right" />
            <ItemTemplate>
                <asp:Label ID="Label1" runat="server" 
                    Text='<%# Bind("UnitPrice", "{0:c}") %>' />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
    OldValuesParameterFormatString="{0}" SelectMethod="GetProducts" 
    TypeName="ProductsBLL" UpdateMethod="UpdateProduct">
    <UpdateParameters>
        <asp:Parameter Name="productName" Type="String" />
        <asp:Parameter Name="unitPrice" Type="Decimal" />
        <asp:Parameter Name="productID" Type="Int32" />
    </UpdateParameters>
</asp:ObjectDataSource>

如图 7 所示,可编辑的 GridView 列出了数据库中每个产品的名称、类别和价格。 花点时间测试页面的功能,对结果进行排序、分页和编辑记录。

每个产品名称、类别和价格都列在可排序、可分页、可编辑的 GridView 中

图 7:每个产品名称、类别和价格都列在可排序、可分页、可编辑的 GridView (单击以查看全尺寸图像)

步骤 3:检查 ObjectDataSource 何时请求数据

Products GridView 通过调用 Select ObjectDataSource 的 ProductsDataSource 方法检索要显示的数据。 此 ObjectDataSource 创建业务逻辑层 类的 ProductsBLL 实例,并调用其 GetProducts() 方法,后者又调用数据访问层 的 ProductsTableAdapterGetProducts() 方法。 DAL 方法连接到 Northwind 数据库并发出配置的 SELECT 查询。 然后,此数据将返回到 DAL,DAL 将其打包在 中 NorthwindDataTable。 DataTable 对象返回到 BLL,后者将其返回到 ObjectDataSource,后者将其返回到 GridView。 然后,GridView 会为 DataRow DataTable 中的每个对象创建一个GridViewRow对象,并且每个GridViewRow对象最终都呈现为 HTML,该 HTML 将返回到客户端并显示在访问者的浏览器中。

每次 GridView 需要绑定到其基础数据时,都会发生此事件序列。 当首次访问页面、从一个数据页移动到另一个数据页、对 GridView 进行排序时,或者通过内置编辑或删除接口修改 GridView 数据时,就会发生这种情况。 如果 GridView 的视图状态处于禁用状态,则 GridView 也会在每次回发时反弹。 GridView 还可以通过调用其 方法显式反弹到其 DataBind() 数据。

为了充分了解从数据库检索数据的频率,让我们显示一条消息,指示何时重新检索数据。 在 GridView 上方添加一个名为 ODSEvents的标签 Web 控件。 清除其 Text 属性并将其属性设置为 EnableViewStatefalse。 在 Label 下面,添加一个 Button Web 控件,并将其 Text 属性设置为 Postback 。

向 GridView 上方的页面添加标签和按钮

图 8:向 GridView 上方的页面添加标签和按钮 (单击以查看全尺寸图像)

在数据访问工作流期间,ObjectDataSource 事件 Selecting 在创建基础对象并调用其配置的方法之前触发。 为此事件创建事件处理程序并添加以下代码:

protected void ProductsDataSource_Selecting(object sender, 
    ObjectDataSourceSelectingEventArgs e)
{
    ODSEvents.Text = "-- Selecting event fired";
}

每次 ObjectDataSource 向体系结构发出数据请求时,Label 将显示文本 Selection 事件触发。

在浏览器中访问此页面。 首次访问页面时,将显示“选择触发事件”文本。 单击“回发”按钮,请注意,如果 GridView 的 EnableViewState 属性设置为 true,则文本将消失 (默认) 。 这是因为,在回发时,GridView 是从其视图状态重新构建的,因此不会转向 ObjectDataSource 获取其数据。 但是,排序、分页或编辑数据会导致 GridView 重新绑定到其数据源,因此“选择”事件触发的文本再次出现。

每当 GridView 反弹到其数据源时,都将显示触发的选择事件

图 9:每当 GridView 被反弹到其数据源时,就会显示“选择”触发的事件 (单击以查看全尺寸图像)

单击回发按钮会使 GridView 从其视图状态重新构造

图 10:单击回发按钮会使 GridView 从其视图状态重新构造 (单击以查看全尺寸图像)

每次对数据进行分页或排序时,检索数据库数据似乎都是浪费的。 毕竟,由于我们使用了默认分页,ObjectDataSource 在显示第一页时检索了所有记录。 即使 GridView 不提供排序和分页支持,每次用户首次访问页面时,都必须从数据库中检索数据, (每次回发(如果视图状态) 禁用)。 但是,如果 GridView 向所有用户显示相同的数据,则这些额外的数据库请求是多余的。 为什么不缓存从 GetProducts() 方法返回的结果,并将 GridView 绑定到那些缓存的结果?

步骤 4:使用 ObjectDataSource 缓存数据

只需设置一些属性,就可以将 ObjectDataSource 配置为在 ASP.NET 数据缓存中自动缓存检索到的数据。 以下列表汇总了 ObjectDataSource 的缓存相关属性:

  • EnableCaching 必须设置为 true 才能启用缓存。 默认为 false
  • CacheDuration 缓存数据的时间量(以秒为单位)。 默认值为 0。 如果 EnableCachingtrueCacheDuration 设置为大于零的值,则 ObjectDataSource 将仅缓存数据。
  • CacheExpirationPolicy 可以设置为 AbsoluteSliding。 如果 Absolute为 ,则 ObjectDataSource 将缓存其检索到的数据数 CacheDuration 秒;如果 Sliding为 ,则数据仅在数秒未访问 CacheDuration 后过期。 默认为 Absolute
  • CacheKeyDependency 使用此属性将 ObjectDataSource 的缓存条目与现有缓存依赖项相关联。 ObjectDataSource 的数据条目可以通过过期其关联的 CacheKeyDependency提前从缓存中逐出。 此属性最常用于将 SQL 缓存依赖项与 ObjectDataSource 的缓存相关联,我们将在将来 使用 SQL 缓存依赖项 教程中探讨这一主题。

让我们将 ProductsDataSource ObjectDataSource 配置为以绝对比例缓存其数据 30 秒。 将 ObjectDataSource 的 EnableCaching 属性设置为 true ,将其 CacheDuration 属性设置为 30。 将 CacheExpirationPolicy 属性设置为其默认值 Absolute

配置 ObjectDataSource 以缓存其数据 30 秒

图 11:配置 ObjectDataSource 以缓存其数据 30 秒 (单击以查看全尺寸图像)

保存所做的更改,并在浏览器中重新访问此页面。 首次访问页面时,将显示选择事件触发的文本,因为最初数据不在缓存中。 但是,通过单击“回发”按钮、排序、分页或单击“编辑”或“取消”按钮触发的后续回发 不会 重新显示“选择”事件触发的文本。 这是因为 Selecting 仅当 ObjectDataSource 从其基础对象获取其数据时才会触发事件; Selecting 如果从数据缓存中拉取数据,则不会触发该事件。

30 秒后,数据将从缓存中逐出。 如果调用 ObjectDataSource 、 InsertUpdateDelete 方法,数据也将从缓存中逐出。 因此,在 30 秒过去或单击“更新”按钮后,排序、分页或单击“编辑”或“取消”按钮将导致 ObjectDataSource 从其基础对象获取其数据,并在事件触发时 Selecting 显示“选择”事件触发的文本。 这些返回的结果将放回到数据缓存中。

注意

如果经常看到 Selection 事件触发文本,即使你期望 ObjectDataSource 处理缓存的数据,也可能是由于内存限制。 如果没有足够的可用内存,则 ObjectDataSource 添加到缓存的数据可能已被清理。 如果 ObjectDataSource 似乎未正确缓存数据或只是偶尔缓存数据,请关闭某些应用程序以释放内存,然后重试。

图 12 演示了 ObjectDataSource 的缓存工作流。 当选择事件触发的文本显示在屏幕上时,这是因为数据不在缓存中,必须从基础对象中检索。 但是,如果缺少此文本,是因为缓存中提供了数据。 从缓存返回数据时,不会调用基础对象,因此不会执行数据库查询。

ObjectDataSource 从数据缓存中存储和检索其数据

图 12:ObjectDataSource 从数据缓存中存储和检索其数据

每个 ASP.NET 应用程序都有自己的数据缓存实例,可在所有页面和访问者之间共享。 这意味着,由 ObjectDataSource 存储在数据缓存中的数据同样在访问页面的所有用户之间共享。 若要验证这一点,请在 ObjectDataSource.aspx 浏览器中打开页面。 首次访问页面时,选择事件触发的文本将显示 (假设之前的测试添加到缓存的数据现已逐出) 。 打开第二个浏览器实例,将 URL 从第一个浏览器实例复制并粘贴到第二个浏览器实例。 在第二个浏览器实例中,不会显示“选择”事件触发的文本,因为它使用与第一个浏览器相同的缓存数据。

将检索到的数据插入缓存时,ObjectDataSource 使用缓存键值,其中包括:CacheDurationCacheExpirationPolicy 属性值;ObjectDataSource 使用的基础业务对象的类型,在本例中,通过 TypeName 属性 (ProductsBLL指定) ;属性的值SelectMethod以及集合中SelectParameters参数的名称和值;以及其 StartRowIndexMaximumRows 属性的值, 实现自定义分页时使用的 。

将这些属性组合创建缓存键值可确保在这些值更改时提供唯一的缓存条目。 例如,在以前的教程中,我们查看了如何使用 ProductsBLL 类 ,该类 GetProductsByCategoryID(categoryID)返回指定类别的所有产品。 一个用户可能会访问页面并查看饮料,其中一个 CategoryID 为 1。 如果 ObjectDataSource 缓存了其结果而不考虑 SelectParameters 值,则当其他用户在饮料产品位于缓存中时来到页面查看调味品时,他们会看到缓存的饮料产品,而不是调味品。 通过通过这些属性(包括 的值 SelectParameters)改变缓存键,ObjectDataSource 为饮料和调味料维护单独的缓存条目。

过时的数据问题

当调用任何一个 、 或 Delete 方法时,UpdateObjectDataSource 会自动从缓存中逐出其Insert项。 这有助于在通过页面修改数据时清除缓存条目,从而防止过时数据。 但是,使用缓存的 ObjectDataSource 仍可能显示过时的数据。 在最简单的情况下,这可能是由于数据直接在数据库中发生更改。 也许数据库管理员刚刚运行了一个脚本来修改数据库中的某些记录。

这种情况也可能以更微妙的方式展开。 调用其中一个数据修改方法时,ObjectDataSource 会从缓存中逐出其项,而删除的缓存项适用于 objectDataSource 属性值的特定组合, (CacheDurationTypeNameSelectMethod、 等) 。 如果你有两个使用不同 SelectMethodsSelectParameters的 ObjectDataSource,但仍可以更新相同的数据,则一个 ObjectDataSource 可能会更新一行并使其自己的缓存条目失效,但第二个 ObjectDataSource 的相应行仍将从缓存提供。 我鼓励你创建页面来展示此功能。 创建一个页面,该页面显示一个可编辑的 GridView,该网格视图从使用缓存的 ObjectDataSource 拉取其数据,并且配置为从 ProductsBLL 类 s GetProducts() 方法获取数据。 将另一个可编辑的 GridView 和 ObjectDataSource 添加到此页面 (或另一个) ,但对于第二个 ObjectDataSource,请使用 GetProductsByCategoryID(categoryID) 方法。 由于这两个 ObjectDataSources SelectMethod 属性不同,因此它们各有自己的缓存值。 如果在一个网格中编辑产品,下次通过分页、排序等方式将数据绑定回另一个网格 () ,它仍将提供旧的缓存数据,不会反映从另一个网格进行的更改。

简言之,仅当愿意有过时数据的可能性时,才使用基于时间的过期时间;对于数据新鲜度非常重要的方案,请使用较短的过期时间。 如果不接受过时数据,请放弃缓存或使用 SQL 缓存依赖项 (假设它是你) 缓存的数据库数据。 我们将在后面的教程中探讨 SQL 缓存依赖项。

总结

在本教程中,我们检查了 ObjectDataSource 的内置缓存功能。 只需设置几个属性,即可指示 ObjectDataSource 将指定 SelectMethod 返回的结果缓存到 ASP.NET 数据缓存中。 CacheDurationCacheExpirationPolicy 属性指示缓存项的持续时间,以及该项是绝对过期还是滑动过期。 属性 CacheKeyDependency 将所有 ObjectDataSource 缓存条目与现有缓存依赖项相关联。 这可用于在达到基于时间的过期之前从缓存中逐出 ObjectDataSource 的条目,并且通常用于 SQL 缓存依赖项。

由于 ObjectDataSource 只是将其值缓存到数据缓存中,因此我们可以以编程方式复制 ObjectDataSource 的内置功能。 在表示层执行此操作没有意义,因为 ObjectDataSource 提供现成的此功能,但我们可以在体系结构的单独层中实现缓存功能。 为此,我们需要重复 ObjectDataSource 使用的相同逻辑。 在下一教程中,我们将探讨如何以编程方式从体系结构内部使用数据缓存。

编程愉快!

深入阅读

有关本教程中讨论的主题的详细信息,请参阅以下资源:

关于作者

Scott Mitchell 是七本 ASP/ASP.NET 书籍的作者, 4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0自学。 可以在 上联系 mitchell@4GuysFromRolla.com他, 也可以通过他的博客联系到他,该博客可在 http://ScottOnWriting.NET中找到。

特别感谢

本教程系列由许多有用的审阅者查看。 本教程的首席审阅者是 Teresa Murphy。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处放置一行 mitchell@4GuysFromRolla.com。