<!-- Adds OutputCache directive -->
<%@ OutputCache Duration="23" VaryByParam="None" %>
它支持五個屬性,其中兩個屬性Duration和VaryByParam是必填的
Duration |
必需屬性。頁面應(yīng)該被緩存的時間,以秒為單位。必須是正整數(shù)。 |
Location |
指定應(yīng)該對輸出進(jìn)行緩存的位置。如果要指定該參數(shù),則必須是下列選項(xiàng)之一:Any、Client、Downstream、None、Server 或 ServerAndClient。 |
VaryByParam |
必需屬性。Request 中變量的名稱,這些變量名應(yīng)該產(chǎn)生單獨(dú)的緩存條目。"none" 表示沒有變動。"*" 可用于為每個不同的變量數(shù)組創(chuàng)建新的緩存條目。變量之間用 ";" 進(jìn)行分隔。 |
VaryByHeader |
基于指定的標(biāo)頭中的變動改變緩存條目。 |
VaryByCustom |
允許在 global.asax 中指定自定義變動(例如,"Browser")。 |
<!-- Sets client OutputCache -->
<%@ OutputCache Duration="23" VaryByParam="None" Location="Client" %>
通過在OutputCache中添加Location屬性,我們實(shí)現(xiàn)了客戶端緩存,通過設(shè)置客戶端緩存我們能夠減少的客戶端請求,也許有人會問:“每個用戶第一次頁面請求都需要服務(wù)器來完成,這不能很好的減少服務(wù)的壓力”。的確是這樣,相對于服務(wù)器緩存,客戶端緩存并沒有減少代碼的執(zhí)行和數(shù)據(jù)庫的操作,但是當(dāng)我們把包含個性化數(shù)據(jù)的頁面緩存在服務(wù)器中,客戶端請求頁面時,由于不同的用戶個性化數(shù)據(jù)不同,這將會導(dǎo)致請求出現(xiàn)錯誤,所以我們可以使用片段緩存把公用的部分緩存起來或客戶端緩存把用戶信息緩存起來。
Query String緩存
在前面的例子中,我們把OutputCache中的VaryByParam屬性設(shè)置為None,那么ASP.NET程序只緩存一個頁面副本;如果頁面請求包含查詢參數(shù),那么在緩存的有效期內(nèi),我們只可以查看到只是緩存結(jié)果,假設(shè)我們有個報(bào)表程序,它提供用戶根據(jù)產(chǎn)品名稱查詢相關(guān)的產(chǎn)品信息。
首先我們創(chuàng)建兩個頁面:查詢和結(jié)果頁面,由于時間關(guān)系我們已經(jīng)把頁面設(shè)計(jì)好了,具體如下所示:
圖2報(bào)表程序
首先我們提供查詢頁面,讓用戶根據(jù)成品名稱(ProductName)查詢相應(yīng)的成品信息,具體的代碼如下:
代碼如下:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
// Gets product id from table Production.Product.
// Then binding data to drop down list control.
InitControl(GetProductId());
}
}
/// <summary>
/// Handles the Click event of the btnSubmit control.
/// Redirects to relative product information page.
/// </summary>
protected void btnSubmit_Click(object sender, EventArgs e)
{
Response.Redirect(string.Format("Product.aspx?productname={0}", ddlProductName.SelectedValue));
}
當(dāng)用戶點(diǎn)擊Submit按鈕后,跳轉(zhuǎn)到Product頁面并且在Url中傳遞查詢參數(shù)——產(chǎn)品名稱(ProducName)。
接下來,我們繼續(xù)完成查詢頁面,由于在前一頁面中傳遞了查詢參數(shù)ProductName,那么我們將根據(jù)ProductName查詢數(shù)據(jù)庫獲取相應(yīng)的產(chǎn)品信息,具體代碼如下所示:
代碼如下:
protected void Page_Load(object sender, EventArgs e)
{
// Get product name.
string productName = Request.QueryString["productname"];
// Binding data to data grid view control.
InitControl(this.GetData(productName));
}
/// <summary>
/// Inits the control.
/// </summary>
/// <param name="ds">The dataset.</param>
private void InitControl(DataSet ds)
{
dgvProduct.DataSource = ds;
dgvProduct.DataBind();
}
/// <summary>
/// Gets the data.
/// </summary>
/// <param name="productName">Name of the product.</param>
/// <returns>Returns dataset</returns>
private DataSet GetData(string productName)
{
// The query sql base on product name.
string sql =
string.Format(
"SELECT Name, ProductNumber, SafetyStockLevel, ReorderPoint, StandardCost, DaysToManufacture "
+ "FROM Production.Product WHERE ProductNumber='{0}'",
productName);
// Get data from table Production.Product.
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLCONN"].ToString()))
using (var com = new SqlCommand(sql, con))
{
com.Connection.Open();
////gdvData.DataSource = com.ExecuteReader();
////gdvData.DataBind();
var ada = new SqlDataAdapter(com);
var ds = new DataSet();
ada.Fill(ds);
return ds;
}
}
前面示例,我們通過Request的屬性QueryString獲取ProductName的值,然后根據(jù)ProductName查詢數(shù)據(jù)庫,最后把獲取數(shù)據(jù)綁定到Datagridview控件中(注:前面實(shí)例沒有考慮SQL Injection問題)。
圖3查詢結(jié)果
現(xiàn)在我們在頁面中添加
<!-- Adds OutputCache directive -->
<%@ OutputCache Duration="30" VaryByParam="None" %>
前面提到當(dāng)
<!-- Sets VaryByParam property-->
<%@ OutputCache Duration="30" VaryByParam="productname" %>
自定義緩存控件
前面我們介紹了通過查詢參數(shù)實(shí)現(xiàn)緩存一個或多個頁面,其實(shí)ASP.NET也允許我們自定義緩存方式來決定是否緩存頁或重用現(xiàn)有的,這時我們可以通過設(shè)置VaryByCustom屬性來實(shí)現(xiàn)。
假設(shè),現(xiàn)在我們要設(shè)計(jì)基于不同UserHostName的緩存,由于程序在執(zhí)行過程中,首先調(diào)用全局方法GetVaryByCustomString()來確定是否緩存頁面或重用現(xiàn)有的,所以我們可以通過重寫GetVaryByCustomString()方法實(shí)現(xiàn)基于UserHostName的緩存,首先我們創(chuàng)建一個Global.asax文件然后重新全局方法GetVaryByCustomString()具體實(shí)現(xiàn)如下:
代碼如下:
/// <summary>
/// Gets vary cache based on custom string value.
/// </summary>
/// <param name="context">Http context.</param>
/// <param name="custom">custom string</param>
/// <returns></returns>
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (string.Equals(custom, "UserHostName", StringComparison.OrdinalIgnoreCase))
{
// Indicates that the cache should be vary on user host name.
return Context.Request.UserHostName;
}
return base.GetVaryByCustomString(context, custom);
}
前面我們重寫了GetVaryByCustomString()方法,使得UserHostName值不同時,獲取相應(yīng)的緩存值。
然后讓程序基于UserHostName創(chuàng)建緩存,所以我們要在頁面添加以下代碼:
代碼如下:
<!-- set vary cache based on custom string value -->
<%@ OutputCache Duration="30" VaryByParam="None" VaryByCustom="UserHostName" %>
我們通過自定義現(xiàn)在GetVaryByCustomString()方法,實(shí)現(xiàn)了Web程序根據(jù)UserHostName實(shí)施不同的緩存方式,其實(shí),我們還可以實(shí)現(xiàn)更多種類緩存方案,例如:基于用戶角色、時間和Url等等。
片段緩存
在某些情況下,我們不能緩存整個頁面,但我們?nèi)韵刖彺娌糠猪撁鎻亩鴾p輕系統(tǒng)的負(fù)擔(dān);其實(shí),我們可以通過兩種方法實(shí)現(xiàn):片段緩存和數(shù)據(jù)緩存.
為了實(shí)現(xiàn)片段緩存,我們需要創(chuàng)建自定義控件緩存部分頁面,然后我們把OutputCache指令添加到自定義控件中,這樣整個頁面將不會被緩存,而自定義緩存控件除外。
前面我們介紹了
<caching>
<!-- Sets out put cache profile-->
<outputCacheSettings>
<outputCacheProfiles>
<add name="ProductCacheProfile" duration="30"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>現(xiàn)在,我們在頁面中添加CacheProfile屬性,并且設(shè)置為ProductCacheProfile,如下所示:
代碼如下:
<!-- set CacheProfile property -->
<%@ OutputCache CacheProfile="ProductCacheProfile" VaryByParam="None" %>
數(shù)據(jù)緩存
Cache對象是線程安全:這表示無需顯式實(shí)現(xiàn)鎖定或解鎖,在添刪Cache對象中的元素,然而,在Cache對象中元素必須是線程安全的。例如,我們創(chuàng)建一個實(shí)體Product,而且存在多個客戶端可能同時操作該對象的情況,這時我們必須為實(shí)體Product實(shí)現(xiàn)鎖定和解鎖操作(同步操作請參考《單例模式(Singleton)的6種實(shí)現(xiàn)》)。
Cache對象中的緩存項(xiàng)自動移除:當(dāng)緩存過期,依賴項(xiàng)被修改或內(nèi)存不足緩存ASP.NET會自動移除該緩存項(xiàng)。
緩存項(xiàng)支持依賴關(guān)系:我們可以給緩存項(xiàng)添加文件、數(shù)據(jù)庫表或其他資源類型的依賴關(guān)系。
SqlDataSource緩存
當(dāng)我們在SqlDataSource控件中啟用緩存,它緩存SelectCommand中的結(jié)果;如果SQL查詢語句中帶有參數(shù)時,SqlDataSource控件會緩存每一個參數(shù)值對應(yīng)的結(jié)果。
這跟我們之前通過
<!-- The product number datasource START -->
<asp:SqlDataSource ID="sourceProductName" runat="server" ProviderName="System.Data.SqlClient"
EnableCaching="True" CacheDuration="3600" ConnectionString="<%$ ConnectionStrings:SQLCONN %>"
SelectCommand="SELECT ProductNumber FROM Production.Product"></asp:SqlDataSource>
<!-- The product number datasource END -->
<!-- The product datasource START -->
<asp:SqlDataSource ID="sourceProduct" runat="server" ProviderName="System.Data.SqlClient"
EnableCaching="True" CacheDuration="3600" ConnectionString="<%$ ConnectionStrings:SQLCONN %>"
SelectCommand="SELECT Name, ProductNumber, SafetyStockLevel, ReorderPoint, StandardCost, DaysToManufacture
FROM Production.Product WHERE ProductNumber=@ProductNumber">
<SelectParameters>
<asp:ControlParameter ControlID="ddlProductNumber" Name="ProductNumber" PropertyName="SelectedValue" />
</SelectParameters>
</asp:SqlDataSource>
<!-- The product number datasource END -->
<!-- Binding the product number to gridview control -->
<!-- NOTE: Due to search and result in the same page, so need to set AutoPostBack is True-->
<asp:DropDownList ID="ddlProductNumber" AutoPostBack="True" DataSourceID="sourceProductName"
DataTextField="ProductNumber" runat="server">
</asp:DropDownList>
<!-- Binding the product datasource to gridview control -->
<asp:GridView ID="gvProduct" runat="server" DataSourceID="sourceProduct" CssClass="Product">
</asp:GridView>
現(xiàn)在我們對報(bào)表程序進(jìn)行查詢,如果ProudctName之前沒有被緩存起來就會創(chuàng)建相應(yīng)的緩存,而已經(jīng)緩存起來的將被重用,查詢結(jié)果如下:
圖6查詢結(jié)果
緩存的依賴關(guān)系
緩存項(xiàng)之間的依賴
ASP.NET Cache允許我們建立緩存之間的依賴關(guān)系,即一個緩存項(xiàng)依賴于另一個緩存項(xiàng);以下示例代碼創(chuàng)建了二個緩存項(xiàng),并且它們之間建立依賴關(guān)系。具體實(shí)現(xiàn)如下:
代碼如下:
// Creates cache object Key1.
Cache["Key1"] = "Cache Item 1";
// Makes Cache["Key2"] dependent on Cache["Key1"].
string[] dependencyKey = new string[1];
dependencyKey[0] = "Key1";
// Creates a CacheDependency object.
CacheDependency dependency = new CacheDependency(null, dependencyKey);
// Establishs dependency between cache Key1 and Key2.
Cache.Insert("Key2", "Cache Item 2", dependency);
現(xiàn)在,當(dāng)Key1緩存項(xiàng)更新或從緩存中刪除,Key2緩存項(xiàng)就會自動從緩存刪除。
文件依賴
前面我們介紹了緩存項(xiàng)之間的依賴關(guān)系,ASP.NET Cache還提供緩存項(xiàng)與文件之間的依賴關(guān)系,當(dāng)文件被更新或刪除對應(yīng)的緩存項(xiàng)也將失效。
在上篇博文《Ajax與JSON的一些總結(jié)》的最后介紹的一個DEMO——Weibo Feed中,我們通過實(shí)時方式向新浪微博API發(fā)送請求獲取相應(yīng)的數(shù)據(jù),但在一定時間內(nèi)請求的次數(shù)是有限制的,一旦超出了限制次數(shù)就不再接受請求了(具體請參考Rate-limiting)。所以可以通過Cache的方式把數(shù)據(jù)緩存起來,當(dāng)客戶端請求時,如果緩存數(shù)據(jù)已經(jīng)存在那么直接返回?cái)?shù)據(jù),否則重新想微博API請求數(shù)據(jù)。
首先,我們創(chuàng)建一個HttpHandler,它負(fù)責(zé)向微博API發(fā)送請求并且把數(shù)據(jù)保存的文件中,最后把數(shù)據(jù)返回的客戶端。
圖7 請求流程
接下來,我們定義CacheData()方法把微博數(shù)據(jù)保存到文本文件中并且建立緩存與數(shù)據(jù)文件的依賴關(guān)系。
代碼如下:
/// <summary>
/// Caches the data into text file.
/// </summary>
/// <param name="context">The http context</param>
private void CacheData(HttpContext context)
{
// Weibo API.
string uri = context.Request.QueryString["api"] + "?" +
"source=" + context.Request.QueryString["source"] + "&" +
"count=" + context.Request.QueryString["count"];
HttpWebResponse response = this.GetWeibos(uri);
if (null == response)
{
throw new ArgumentNullException("Response is null");
}
string jsonData;
// Writes the reponse data into text file.
using (var reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(response.CharacterSet)))
{
jsonData = reader.ReadToEnd();
}
string dataPath = context.Server.MapPath("weibo.json");
using (var writer = new StreamWriter(dataPath, false, Encoding.GetEncoding(response.CharacterSet)))
{
writer.Write(jsonData);
}
// Establishs dependency between cache weibo and text file.
// Sets cache expires after 2 minuntes.
HttpRuntime.Cache.Insert("weibo", jsonData, Dep, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(2));
}
現(xiàn)在我們把數(shù)據(jù)保存到文本文件中并且建立了緩存weibo與數(shù)據(jù)文件的依賴關(guān)系,接下來我們要把JSON格式數(shù)據(jù)返回給客戶端。
代碼如下:
/// <summary>
/// Responses the weibo data.
/// </summary>
/// <param name="context">The http contex.</param>
private void ResponseWeibo(HttpContext context)
{
// Gets the weibo cache data.
byte[] buf = Encoding.UTF8.GetBytes(HttpRuntime.Cache["weibo"].ToString());
// Writes the data into output stream.
context.Response.OutputStream.Write(buf, 0, buf.Length);
context.Response.OutputStream.Flush();
////context.Response.Close();
}
上面我們把JSON格式字符串轉(zhuǎn)換為Byte數(shù)值,然后寫入到OutputStream中,最后把數(shù)據(jù)返回給客戶端。
代碼如下:
// The function to get weibo data.
loadWeibo: function() {
$.ajax({
// Weibo API.
url: "WeiboHandler.ashx",
type: "GET",
// NOTE: We get the data from same domain,
// dataType is json.
dataType: "json",
data: {
source: JQWeibo.appKey,
count: JQWeibo.numWeibo
},
// When the requet completed, then invokes success function.
success: function(data, textStatus, xhr) {
// Sets html structure.
var html =
'<div class="weibo">' +
'<a target="_blank">USER</a>' +
':WEIBO_TEXT<div class="time">AGO</div>';
// Appends weibos into html page.
for (var i = 0; i < data.length; i++) {
$(JQWeibo.appendTo).append(
html.replace('WEIBO_TEXT', JQWeibo.ify.clean(data[i].text))
// Uses regex and declare DOMAIN as global, if found replace all.
.replace(/DOMAIN/g, data[i].user.domain)
.replace(/USER/g, data[i].user.screen_name)
.replace('AGO', JQWeibo.timeAgo(data[i].created_at))
);
}
}
})
}
圖8請求結(jié)果
1.1.3 總結(jié)
緩存可以使應(yīng)用程序的性能得到很大的提高,因此在設(shè)計(jì)應(yīng)用程序應(yīng)該予以考慮,本博文主要介紹了ASP.NET中
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com