ASP.NET夜話之二十一asp.net網(wǎng)站的性能優(yōu)化精品c#文章 2011-01-13 22:57:46 閱讀85 評(píng)論0 字號(hào):大中小 訂閱 l 程序編碼優(yōu)化 l 數(shù)據(jù)操作優(yōu)化 l 配置優(yōu)化 l 總結(jié) 21.1 程序編碼優(yōu)化 21.1.1 集合操作 在選擇集合類型時(shí)應(yīng)考慮幾點(diǎn): (1)集合中的元素類型是否是一致的,比如集合中將要存儲(chǔ)的元素都是int或者都是string類型的就可以考慮使用數(shù)組或者泛型集合,這樣在存儲(chǔ)數(shù)值類型元素就可以避免裝箱拆箱操作,即使是引用類型的元素也可以避免類型轉(zhuǎn)換操作。 (2)集合中的元素個(gè)數(shù)是否是固定的,如果集合中存儲(chǔ)的元素是固定的并且元素類型是一致的就可以使用數(shù)組來存儲(chǔ)。 (3)將來對(duì)集合的操作集中在那些方面,如果對(duì)集合的操作以查找居多可以考慮HashTable或者Dictionary<TKey,TValue>這樣的集合,因?yàn)樵?NET Framework中對(duì)這類集合采用了特殊機(jī)制,所以在查找時(shí)比較的次數(shù)比其它集合要少。 另外,在使用可變集合時(shí)如果不制定初始容量大小,系統(tǒng)會(huì)使用一個(gè)默認(rèn)值來指定可變集合的初始容量大小,如果將來元素個(gè)數(shù)超過初始容量大小就會(huì)先在內(nèi)部重新構(gòu)建一個(gè)集合,再將原來集合中的元素復(fù)制到新集合中,可以在實(shí)例化可變集合時(shí)指定一個(gè)相對(duì)較大的初始容量,這樣在向可變集合中添加大量元素時(shí)就可以避免集合擴(kuò)充容量帶來的性能損失。 下面以一個(gè)例子演示一下數(shù)組、ArrayList及List<T>集合操作的例子。頁面的設(shè)計(jì)代碼如下: using System; using System.Collections; using System.Collections.Generic; /// <summary> /// 用來測(cè)試對(duì)集合進(jìn)行操作所花費(fèi)的時(shí)間 /// </summary> public class CollectionDemo { public static void Main() { Test(100000); Console.WriteLine("=========="); Test(1000000); Console.WriteLine("=========="); Test(10000000); Console.ReadLine(); } /// <summary> /// 操作數(shù)組 /// </summary> /// <param name="maxCount">要操作的次數(shù)</param> /// <returns></returns> private static TimeSpan ArrayOperation(int maxCount) { //在程序開始運(yùn)行時(shí)記錄下系統(tǒng)當(dāng)前時(shí)間 DateTime start = DateTime.Now; int[] intList = new int[maxCount]; int j = 0; for (int i = 0; i < maxCount; i++) { intList[i] = i; } for (int i = 0; i < maxCount; i++) { j = intList[i]; } //在程序結(jié)束后記錄下系統(tǒng)當(dāng)前時(shí)間 DateTime end = DateTime.Now; //用程序結(jié)束的系統(tǒng)時(shí)間減去開始運(yùn)行時(shí)的時(shí)間就是代碼運(yùn)行時(shí)間 return end - start; } /// <summary> /// /// </summary> /// <param name="maxCount"></param> /// <returns></returns> private static TimeSpan ArrayListOperation(int maxCount) { //在程序開始運(yùn)行時(shí)記錄下系統(tǒng)當(dāng)前時(shí)間 DateTime start = DateTime.Now; //用默認(rèn)的容量來初始化ArrayList //ArrayList intList = new ArrayList(); //用指定的容量來初始化ArrayList ArrayList intList = new ArrayList(maxCount); int j = 0; for (int i = 0; i < maxCount; i++) { intList.Add(i); } for (int i = 0; i < maxCount; i++) { j = (int)intList[i]; } //在程序結(jié)束后記錄下系統(tǒng)當(dāng)前時(shí)間 DateTime end = DateTime.Now; return end - start; } /// <summary> /// /// </summary> /// <param name="maxCount"></param> /// <returns></returns> private static TimeSpan GenericListOperation(int maxCount) { //在程序開始運(yùn)行時(shí)記錄下系統(tǒng)當(dāng)前時(shí)間 DateTime start = DateTime.Now; //用默認(rèn)的容量來初始化泛型集合 //List<int> intList = new List<int>(); //用指定的容量來初始化泛型集合 List<int> intList = new List<int>(maxCount); int j = 0; for (int i = 0; i < maxCount; i++) { intList.Add(i); } for (int i = 0; i < maxCount; i++) { j = intList[i]; } //在程序結(jié)束后記錄下系統(tǒng)當(dāng)前時(shí)間 DateTime end = DateTime.Now; return end - start; }
private static void Test(int maxCount) { TimeSpan ts1 = ArrayOperation(maxCount); TimeSpan ts2 = ArrayListOperation(maxCount); TimeSpan ts3 = GenericListOperation(maxCount); Console.WriteLine("執(zhí)行" + maxCount + "次操作:"); Console.WriteLine("數(shù)組耗時(shí)" + ts1.TotalMilliseconds + "毫秒"); Console.WriteLine("ArrayList耗時(shí)" + ts2.TotalMilliseconds + "毫秒"); Console.WriteLine("泛型集合耗時(shí)" + ts3.TotalMilliseconds + "毫秒"); } } 對(duì)上面的程序代碼做幾點(diǎn)說明: (1)上面的代碼僅僅是給集合中的元素賦值,然后將集合中的元素取出來,分別用了數(shù)組、ArrayList和List<T>泛型集合,并且操作了不同的次數(shù)。 (2)在開始運(yùn)行時(shí)獲取到系統(tǒng)的當(dāng)前時(shí)間,然后在運(yùn)行結(jié)束之后再次獲取系統(tǒng)時(shí)間,兩次時(shí)間之差就是程序運(yùn)行這段代碼所花費(fèi)的時(shí)間,這是一個(gè)TimeSpan類型的變量。 (3)為了將測(cè)試結(jié)果放大,所以操作的次數(shù)要盡量設(shè)置大一點(diǎn),實(shí)際在網(wǎng)站運(yùn)行中程序代碼也會(huì)被成千上萬次運(yùn)行,所以這么做是可以接受的,也使得比較更明顯,并且這樣也可以減小某些偶然因素帶來的干擾。 因?yàn)樵贏SP.NET中測(cè)試不穩(wěn)定因素太多,所以這部分代碼是以控制臺(tái)程序來運(yùn)行的,運(yùn)行上面的代碼可得到如圖21-1所示的效果:
圖21-1 程序執(zhí)行結(jié)果 在上面的代碼中我們是采用了指定ArrayList和List<int>泛型集合的初始化容量大小,可以看出操作在集合元素固定的情況下,數(shù)組的操作是最快的,泛型集合的操作次之,ArrayList最慢。 以上測(cè)試是針對(duì)值類型數(shù)據(jù)的測(cè)試,如果是String這類的引用類型也會(huì)有類似的效果,只不過效果引用類型作為集合元素沒有值類型作為集合元素明顯。 21.1.2 字符串連接優(yōu)化 看如下代碼: String a=”abc”; String b=”abc”; 在上面的代碼中變量a和變量b都指向了堆中的同一個(gè)引用,也就是和下面的代碼是等效的: String a=”abc”; String b=a; 在給字符串變量賦值時(shí)會(huì)首先在“拘留池”中檢查是否有與要賦值的值相等的字符串,如果存在就會(huì)返回該字符串的引用,如果不存在就向字符串“駐留池”中添加該字符串,并且將該字符串的引用返回。這樣一來在每次連接字符串時(shí)都有可能創(chuàng)建新的字符串對(duì)象(如果“駐留池”中不存在對(duì)應(yīng)的字符串的話),從而導(dǎo)致了性能低下。 在String類有個(gè)方法專門用來檢測(cè)“拘留池”中是否存在指定字符串引用的方法,這個(gè)方法就是IsInterned(string str)方法,如果存在這個(gè)引用則返回str的引用,如果不存在這個(gè)引用就返回null。 在需要多次連接字符串時(shí)可以考慮使用System.Text.StringBuilder對(duì)象,這是一個(gè)可變?nèi)萘康淖址畬?duì)象。在實(shí)例化StringBuilder對(duì)象時(shí)會(huì)指定一個(gè)容量(如果不顯示指定,則系統(tǒng)默認(rèn)會(huì)指定初始容量為16,如果在程序中最終連接后的容量大于這個(gè)值可以自行指定一個(gè)較大的值作為初時(shí)容量,這樣也能提高性能),在進(jìn)行添加、插入及替換等修改操作時(shí)如果不超過容量,則會(huì)直接在緩沖區(qū)中操作,如果超過容量則會(huì)重新分配一個(gè)更大的緩沖區(qū),并將原來的數(shù)據(jù)復(fù)制到新緩沖區(qū)。 下面通過一個(gè)控制臺(tái)的例子來演示一下String類和StringBuilder類的區(qū)別,代碼如下: using System; using System.Text;
public class StringDemo { public static void Main() { //如果存在"abc"字符串則a="abc",否則a=null; string a = string.IsInterned("abc"); string b = new StringBuilder().Append("as").Append("df").ToString(); if (string.IsNullOrEmpty(a)) { Console.WriteLine(a + "不存在'拘留池’中"); } else { Console.WriteLine(a + "存在'拘留池’中"); } if (string.IsNullOrEmpty(b)) { Console.WriteLine(b + "不存在'拘留池’中"); } else { Console.WriteLine(b + "存在'拘留池’中"); }
int count = 9000; TimeSpan ts1 = StringConcat(count); TimeSpan ts2 = StringBuilderConcat(count); Console.WriteLine("使用了String來連接"+count.ToString()+"次耗時(shí)"+ts1.TotalMilliseconds+"毫秒"); Console.WriteLine("使用StringBuilder來連接" + count.ToString() + "次耗時(shí)" + ts2.TotalMilliseconds + "毫秒"); Console.ReadLine(); } /// <summary> /// 用String對(duì)象來連接字符串 /// </summary> /// <param name="count"></param> /// <returns></returns> public static TimeSpan StringConcat(int count) { DateTime start = DateTime.Now; string text = string.Empty; for (int i = 0; i < count; i++) { text=text+"0"+i; } DateTime end = DateTime.Now; return end - start; } /// <summary> /// 用StringBuilder對(duì)象來連接字符串 /// </summary> /// <param name="count"></param> /// <returns></returns> public static TimeSpan StringBuilderConcat(int count) { DateTime start = DateTime.Now; StringBuilder text = new StringBuilder(); for (int i = 0; i < count; i++) { text.Append("0"+i); } DateTime end = DateTime.Now; return end - start; } } 這個(gè)程序的運(yùn)行效果如圖21-2所示:
圖21-2 String類和StringBuilder類連接字符串的運(yùn)行效果 21.1.3 類型轉(zhuǎn)換優(yōu)化 21.1.3.1 字符串類型向值類型轉(zhuǎn)換 其用法如下面的代碼所示: string str1 = "123"; string str2 = "ed1"; //因?yàn)樽鳛閛ut參數(shù),所以即使是據(jù)不變量也不用賦值 int number1; //因?yàn)樽鳛閛ut參數(shù),所以即使是據(jù)不變量也不用賦值 int number2; //"123"能轉(zhuǎn)換成int,所以b1=true,number1=123 bool b1 = int.TryParse(str1, out number1); //"ed1"不能轉(zhuǎn)換成int,所以b2=false bool b2 = int.TryParse(str2, out number2);
21.1.3.2 引用類型之間轉(zhuǎn)換 在引用類型之間轉(zhuǎn)換有兩種方式:強(qiáng)制轉(zhuǎn)換和as轉(zhuǎn)換。下面是強(qiáng)制轉(zhuǎn)換的例子: object d = "asdf"; //將d牽制轉(zhuǎn)換成string類型 string e = (string)d; 同字符串類型轉(zhuǎn)換成數(shù)值類型一樣,如果不存在對(duì)應(yīng)的轉(zhuǎn)換關(guān)系也會(huì)拋出異常。為了避免引用類型之間轉(zhuǎn)換拋出異常,可以使用as關(guān)鍵字來轉(zhuǎn)換,如下面的代碼: object d ="123"; //下面的轉(zhuǎn)換會(huì)拋出異常 StringBuilder e = (StringBuilder)d; //下面的轉(zhuǎn)換不會(huì)拋出異常,并且f為null StringBuilder f = d as StringBuilder; 在C#中還有一個(gè)is關(guān)鍵字,它用來檢查對(duì)象是否與給定類型兼容,如果兼容表達(dá)式的值為true,否則為false。例如下面的表達(dá)式: string a = "asdf"; //b1=true,因?yàn)閟tring類實(shí)現(xiàn)了ICloneable接口 bool b1 = a is ICloneable; //b2=true,因?yàn)閟tring類是object類的派生類 bool b2 = a is object; //b3=false,因?yàn)閟tring類與StringBuilder類之間不存在派生或者實(shí)現(xiàn)關(guān)系 bool b3 = a is StringBuilder; 假如有A類型的變量a和B類型,對(duì)于A c=a as B這個(gè)轉(zhuǎn)換,存在如下情況:如果A實(shí)現(xiàn)或者派生自B,那么上面的轉(zhuǎn)換成功,否則轉(zhuǎn)換不成功,c為null,并且不會(huì)拋出異常。 上面講到的都是關(guān)于編碼方面提高程序性能應(yīng)該注意的實(shí)現(xiàn),此外在編碼過程中還應(yīng)注意盡量減少裝箱拆箱操作。裝箱操作是指將值類型轉(zhuǎn)換成引用類型,拆箱操作是指將引用類型轉(zhuǎn)換成值類型,通過裝箱操作使得值類型可以被視作對(duì)象。相對(duì)于簡(jiǎn)單的賦值而言,裝箱和取消裝箱過程需要進(jìn)行大量的計(jì)算。對(duì)值類型進(jìn)行裝箱時(shí),必須分配并構(gòu)造一個(gè)全新的對(duì)象。次之,取消裝箱所需的強(qiáng)制轉(zhuǎn)換也需要進(jìn)行大量的計(jì)算。在向ArrayList這樣的非范型集合中添加值類型元素時(shí)就會(huì)存在裝箱過程,再?gòu)姆欠缎图现腥〕鲋殿愋偷闹稻蜁?huì)存在拆箱過程。 21.1.4 使用Server.Transfer()方法 21.1.5 避免不必要的服務(wù)器往返 21.1.6 盡早釋放對(duì)象 21.1.7 盡量減少服務(wù)器控件的使用 21.2 數(shù)據(jù)操作優(yōu)化 21.2.1 數(shù)據(jù)庫(kù)連接對(duì)象使用優(yōu)化 除此之外,還可以使用數(shù)據(jù)庫(kù)連接池來優(yōu)化。連接到數(shù)據(jù)庫(kù)通常需要幾個(gè)需要很長(zhǎng)時(shí)間的步驟組成,如建立物理通道(例如套接字或命名管道)、與服務(wù)器進(jìn)行初次握手、分析連接字符串信息、由服務(wù)器對(duì)連接進(jìn)行身份驗(yàn)證、運(yùn)行檢查以便在當(dāng)前事務(wù)中登記等等。實(shí)際上,大多數(shù)應(yīng)用程序僅使用一個(gè)或幾個(gè)不同的連接配置。這意味著在執(zhí)行應(yīng)用程序期間,許多相同的連接將反復(fù)地打開和關(guān)閉。為了使打開的連接成本最低,ADO.NET 使用稱為連接池的優(yōu)化方法。連接池減少新連接需要打開的次數(shù)。池進(jìn)程保持物理連接的所有權(quán)。通過為每個(gè)給定的連接配置保留一組活動(dòng)連接來管理連接。只要用戶在連接上調(diào)用 Open,池進(jìn)程就會(huì)檢查池中是否有可用的連接。如果某個(gè)池連接可用,會(huì)將該連接返回給調(diào)用者,而不是打開新連接。應(yīng)用程序在該連接上調(diào)用 Close 時(shí),池進(jìn)程會(huì)將連接返回到活動(dòng)連接池集中,而不是真正關(guān)閉連接。連接返回到池中之后,即可在下一個(gè) Open 調(diào)用中重復(fù)使用。 池連接可以大大提高應(yīng)用程序的性能和可縮放性。默認(rèn)情況下,ADO.NET 中啟用連接池。除非顯式禁用,否則,連接在應(yīng)用程序中打開和關(guān)閉時(shí),池進(jìn)程將對(duì)連接進(jìn)行優(yōu)化。在開發(fā)大型網(wǎng)站時(shí)可以更改默認(rèn)的數(shù)據(jù)庫(kù)連接池配置信息,例如可以增加數(shù)據(jù)庫(kù)連接池的最大連接數(shù)(默認(rèn)是100),如下面的代碼就是將數(shù)據(jù)庫(kù)連接池的最大連接數(shù)設(shè)為200: Data Source=(local);Initial Catalog=AspNetStudy;User ID=sa;Password=sa;Pooling=true;Min Pool Size=0;Max Pool Size=200 當(dāng)然也不是設(shè)置數(shù)據(jù)庫(kù)連接池的最大連接數(shù)越大越好,實(shí)際上還會(huì)受其它因素的限制。 21.2.2 數(shù)據(jù)訪問優(yōu)化 此外,在數(shù)據(jù)訪問時(shí)還可以使用存儲(chǔ)過程。使用存儲(chǔ)過程除了可以防范SQL注入之外,還可以提高程序性能和減少網(wǎng)絡(luò)流量。存儲(chǔ)過程是存儲(chǔ)在服務(wù)器上的一組預(yù)編譯的SQL語句,具有對(duì)數(shù)據(jù)庫(kù)立即訪問的功能,信息處理極為迅速。使用存儲(chǔ)過程可以避免對(duì)命令的多次編譯,在執(zhí)行一次后其執(zhí)行規(guī)劃就駐留在高速緩存中,以后需要時(shí)只需直接調(diào)用緩存中的二進(jìn)制代碼即可。 21.2.3 優(yōu)化SQL語句 在優(yōu)化SQL語句時(shí),有幾條原則需要注意: (1)盡量避免”select * from 表名”這樣的SQL語句,特別是在表中字段比較多而只需要顯示某幾個(gè)字段數(shù)據(jù)的情況下更應(yīng)該注意這個(gè)問題,比如針對(duì)SQL Server數(shù)據(jù)庫(kù)來說,如果不需要顯示或者操作表中的image、Text、ntext及xml這樣的字段,就盡量不要出現(xiàn)在select語句中的字段列表中。 (2)盡量不要在查詢語句中使用子查詢。 (3)盡量使用索引。索引是與表或視圖關(guān)聯(lián)的磁盤上結(jié)構(gòu),可以加快從表或視圖中檢索行的速度。索引包含由表或視圖中的一列或多列生成的鍵。這些鍵存儲(chǔ)在一個(gè)結(jié)構(gòu)中,使數(shù)據(jù)庫(kù)可以快速有效地查找與鍵值關(guān)聯(lián)的行。設(shè)計(jì)良好的索引可以減少磁盤 I/O 操作,并且消耗的系統(tǒng)資源也較少,從而可以提高查詢性能。對(duì)于包含 SELECT、UPDATE 或 DELETE 語句的各種查詢,索引會(huì)很有用。查詢優(yōu)化器使用索引時(shí),搜索索引鍵列,查找到查詢所需行的存儲(chǔ)位置,然后從該位置提取匹配行。通常,搜索索引比搜索表要快很多,因?yàn)樗饕c表不同,一般每行包含的列非常少,且行遵循排序順序。對(duì)于常用作where查詢的字段可以建立索引以提高查詢速度。注意,使用索引后會(huì)降低對(duì)表的插入、更新和刪除速度,在一張表上也不宜建立過多的索引。 21.2.4 合理使用緩存 除此之外,還有System.Web.Caching.Cache類對(duì)提高程序性能也非常有用,雖然利用Session或者Application也能實(shí)現(xiàn)在內(nèi)存中保存數(shù)據(jù),但是在Session中保存的數(shù)據(jù)只能被單個(gè)用戶使用,而在Application中使用的數(shù)據(jù)如果不手動(dòng)釋放就會(huì)一直保存在內(nèi)存當(dāng)中,利用Cache就完全克服了上面的缺點(diǎn)。Cache類提供了強(qiáng)大的功能,允許自定義緩存項(xiàng)及緩存時(shí)間和優(yōu)先級(jí)等,在服務(wù)器內(nèi)存不夠用時(shí)會(huì)自動(dòng)較少使用的或者優(yōu)先級(jí)比較低的項(xiàng)以釋放內(nèi)存。另外還可以指定緩存關(guān)聯(lián)依賴項(xiàng),如果緩存關(guān)聯(lián)依賴項(xiàng)發(fā)生改變緩存項(xiàng)就會(huì)實(shí)效并從緩存中移除。比如可以將一個(gè)經(jīng)常要讀取的文件的內(nèi)容緩存起來,并在文件上保留一個(gè)依賴項(xiàng),一旦文件內(nèi)容發(fā)生變化就會(huì)從內(nèi)存中移除緩存的文件內(nèi)容,可以再次從文件中重新讀取文件內(nèi)容到緩存中,這樣就保證了得到的文件內(nèi)容是最新的。 下面就是一個(gè)在ASP.NET使用Cache的例子,頁面的設(shè)計(jì)部分代碼如下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="CacheDemo.aspx.cs" Inherits="day21_CacheDemo" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www./TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www./1999/xhtml" > <head runat="server"> <title>使用Cache的例子</title> </head> <body> <form id="form1" runat="server"> <div> <asp:TextBox ID="txtContent" runat="server" Columns="50" Rows="10" TextMode="MultiLine"></asp:TextBox> <asp:Button ID="btnGetFileContent" runat="server" OnClick="btnGetFileContent_Click" Text="顯示文件內(nèi)容" />
</div> </form> </body> </html> 頁面的邏輯代碼如下: using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.IO; using System.Text; using System.Web.Caching;
public partial class day21_CacheDemo : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) {
} protected void btnGetFileContent_Click(object sender, EventArgs e) { //從緩存中根據(jù)鍵讀取,并使用as轉(zhuǎn)換 string content = Cache["Content"] as string; //如果不存在緩存項(xiàng),則設(shè)置緩存 if (content== null) { String path = Server.MapPath("~/day21/Content.txt"); //注意這里沒有使用FileStream和StreamReader來讀取文件內(nèi)容 string text = File.ReadAllText(path, Encoding.UTF8); //創(chuàng)建緩存依賴項(xiàng) CacheDependency dependency = new CacheDependency(path); //創(chuàng)建當(dāng)緩存移除時(shí)調(diào)用的方法 CacheItemRemovedCallback callBack = new CacheItemRemovedCallback(ItemRemovedCallBack); //添加緩存,并且設(shè)定緩存過期時(shí)間為30分鐘 Cache.Add("Content", text, dependency, Cache.NoAbsoluteExpiration, new TimeSpan(0, 30, 0), CacheItemPriority.Default, callBack); txtContent.Text = text; } else { txtContent.Text = content; } } /// <summary> /// 當(dāng)從緩存中移除緩存項(xiàng)時(shí)要調(diào)用的方法 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="reason"></param> private void ItemRemovedCallBack(string key, object value, CacheItemRemovedReason reason) { String path = Server.MapPath("~/day21/CacheChangeLog.txt"); string log=string.Format("鍵為{0}的緩存項(xiàng)于{1}被刪除,原因是:{2}。\r\n", key,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ms"),reason.ToString()); //如果不存在文件下面的方法會(huì)創(chuàng)建文件并將內(nèi)容寫入到文件 //如果存在文件就會(huì)追加 File.AppendAllText(path, log); } }
為了達(dá)到演示效果,還需要在頁面所在文件夾下創(chuàng)建一個(gè)txt文件,文件內(nèi)容為“這是《ASP.NET夜話》第二十一章中進(jìn)行的Cache測(cè)試文件。”,并用UTF-8編碼保存,如圖21-3所示:
圖21-3 創(chuàng)建Cotent.txt文件并保存成UTF-8編碼 運(yùn)行頁面之后的效果如圖21-4所示:
圖21-4 頁面的初始運(yùn)行效果 點(diǎn)擊“顯示文件內(nèi)容”按鈕,因?yàn)槭状物@示在緩存中并不存在文件內(nèi)容,所以會(huì)將文件內(nèi)容讀取出來并在文本框中顯示。在文本框顯示了文件內(nèi)容之后,手動(dòng)修改Content.txt文件的內(nèi)容并保存,然后再次點(diǎn)擊顯示文件內(nèi)容,在文本框中就會(huì)顯示文件的最新內(nèi)容了,如圖21-5所示:
圖21-5 在修改文件內(nèi)容之后重新顯示文件內(nèi)容的效果 由此可見,使用了緩存關(guān)聯(lián)依賴項(xiàng)之后確實(shí)能移除緩存數(shù)據(jù),下次顯示時(shí)因?yàn)榫彺骓?xiàng)已經(jīng)被移除所以會(huì)重新讀取文件內(nèi)容并進(jìn)行緩存,因而就能看到最新的文件內(nèi)容。同時(shí),還能在Content.txt文件所在文件夾下看到一個(gè)CacheChangeLog.txt文件,這個(gè)文件的內(nèi)容如圖21-6所示:
圖21-6 CacheChangeLog.txt文件的內(nèi)容 總之,Cache對(duì)象是一個(gè)使用起來很靈活的對(duì)象,可以滿足復(fù)雜條件下的數(shù)據(jù)緩存要求,合理使用緩存有時(shí)候能提高數(shù)量級(jí)的性能。不過在使用緩存時(shí)也要注意一些事項(xiàng),比如不要緩存頻繁變化和很少使用的數(shù)據(jù),也不要將數(shù)據(jù)緩存的時(shí)間設(shè)置過短,否則不但不能提高性能,嚴(yán)重情況下反而會(huì)降低性能。 21.3 配置優(yōu)化 21.3.1 禁用調(diào)試模式 <compilation debug="false"> 21.3.2 合理使用ViewState 下面是一個(gè)使用GridView控件來顯示數(shù)據(jù)的例子,這個(gè)文件其實(shí)在講述GridView控件時(shí)曾用來作為例子,現(xiàn)在又再次用來作為例子,代碼如下: <%@ Page Language="C#" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www./TR/xhtml1/DTD/xhtml1-transitional.dtd">
<mce:script runat="server"><!-- protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { //默認(rèn)顯示第一頁,不過在GridView中第一頁的頁索引是0 //注意:在C#中集合里的索引也都是以0開始 BindGridView(0); } } //指定綁定頁面的數(shù)據(jù) private void BindGridView(int pageIndex) { //實(shí)例化Connection對(duì)象 SqlConnection connection = new SqlConnection("Data Source=zhou\\sql2000;Initial Catalog=AspNetStudy;Persist Security Info=True;User ID=sa;Password=sa"); //實(shí)例化Command對(duì)象 SqlCommand command = new SqlCommand("select * from UserInfo", connection); SqlDataAdapter adapter = new SqlDataAdapter(command); DataTable data = new DataTable(); adapter.Fill(data);
#region 注意這部分代碼可以在設(shè)計(jì)視圖中設(shè)置,不必寫在代碼里 gvUserList.AllowPaging = true;//設(shè)置允許自動(dòng)分頁 //gvUserList.AutoGenerateColumns = false;//設(shè)置不允許自動(dòng)綁定列 gvUserList.PageSize = 5;//設(shè)置每頁顯示5條記錄 #endregion
gvUserList.DataSource = data; gvUserList.PageIndex = pageIndex;//設(shè)置當(dāng)前顯示第幾頁 gvUserList.DataBind(); }
//翻頁事件 protected void gvUserList_PageIndexChanging(object sender, GridViewPageEventArgs e) { //指定新頁面,重新綁定數(shù)據(jù) BindGridView(e.NewPageIndex); }
<html xmlns="http://www./1999/xhtml" > <head runat="server"> <title>用GridView顯示數(shù)據(jù)的例子(注意:采用單頁模式)</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView ID="gvUserList" runat="server" AutoGenerateColumns="False" OnPageIndexChanging="gvUserList_PageIndexChanging"> <Columns> <asp:BoundField DataField="UserId" HeaderText="編號(hào)" /> <asp:HyperLinkField DataNavigateUrlFields="UserId" DataNavigateUrlFormatString="DetailsViewDemo.aspx?UserId={0}" DataTextField="RealName" HeaderText="查看" /> <asp:BoundField DataField="UserName" HeaderText="用戶名" > <ItemStyle BackColor="#C0FFC0" /> </asp:BoundField> <asp:BoundField DataField="RealName" HeaderText="真實(shí)姓名" /> <asp:BoundField DataField="Age" HeaderText="年齡" /> <asp:CheckBoxField DataField="Sex" HeaderText="男" /> <asp:BoundField DataField="Mobile" HeaderText="手機(jī)" /> <asp:TemplateField HeaderText="電子郵件"> <AlternatingItemTemplate> <a href='emailto:<%#Eval("Email") %>'>發(fā)郵件給<%#Eval("RealName") %></a> </AlternatingItemTemplate> <ItemTemplate> <%#Eval("Email") %> </ItemTemplate> </asp:TemplateField> </Columns> <EmptyDataTemplate> 溫馨提示:當(dāng)前沒有任何記錄哦。 </EmptyDataTemplate> </asp:GridView>
</div> </form> </body> </html> 運(yùn)行這個(gè)頁面會(huì)看到如圖21-7所示的效果。
圖21-7 用GridView顯示用戶信息 如果看到這個(gè)頁面的最終生成的HTML頁面源代碼,會(huì)看到如圖21-8顯示的效果:
圖21-8 查看最終HTML頁面的源代碼效果 從上圖可以看出為了維護(hù)GridView控件的狀態(tài)所花費(fèi)的代價(jià)是客觀的,如果不需要維護(hù)這個(gè)狀態(tài)可以禁用GridView控件的視圖狀態(tài)(在大部分情況下都用不著)。具體操作是切換到設(shè)計(jì)視圖下,在頁面中選中GridView控件然后在屬性窗口中找到EnableViewState屬性并將其設(shè)為false,如圖21-9所示:
圖21-9 禁用GridView控件的視圖狀態(tài) 禁用GridView的視圖狀態(tài)之后再次運(yùn)行頁面并查看HTML源代碼,會(huì)看到如圖21-10所示的效果:
圖21-10 禁用GridView的視圖狀態(tài)之后生成的HTML源代碼 從圖21-8和圖21-10對(duì)比情況來看,禁用了視圖狀態(tài)之后生成的HTML代碼大小大大減少,不斷降低了網(wǎng)絡(luò)流量傳輸,還減輕了服務(wù)器的負(fù)擔(dān)。如果要禁用整個(gè)頁面的服務(wù)器控件的視圖狀態(tài),可以在頁面@Page指令中添加EnableViewState="false"值,如本實(shí)例中頁面禁用頁面中所有控件視圖狀態(tài)之后,頁面的@Page指令如下: <%@ Page Language="C#" EnableViewState="false" %> 最后要提示一點(diǎn)的是,禁用服務(wù)器控件的視圖狀態(tài)之后有可能有些服務(wù)器控件的內(nèi)置功能無法使用,例如在本利中禁用GridView控件的視圖狀態(tài)之后就沒有辦法使用內(nèi)置分頁功能了。 21.3.3 合理選擇會(huì)話狀態(tài)存儲(chǔ)方式 數(shù)據(jù)存儲(chǔ)模式 上面的每種方式都有字節(jié)的優(yōu)點(diǎn)和缺點(diǎn),InProc 模式是存儲(chǔ)會(huì)話狀態(tài)最快的解決方案,如果不需要保存大量數(shù)據(jù)并且不需要Web服務(wù)器重其后還能保存數(shù)據(jù),則建議使用這種方式。例如下面的設(shè)置就是用了使用了進(jìn)程內(nèi)存儲(chǔ)會(huì)話狀態(tài),并且設(shè)置Session的超期時(shí)間為20分鐘。 view plaincopy to clipboardprint? <system.web> <sessionState mode="InProc" timeout="20"/> </system.web> </configuration> 21.4 總結(jié)
--------------------------------------------------------------------------------
此外,上面提到的方法要注意其使用場(chǎng)合。 |
|