一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

ASP.NET夜話之二十一asp.net網(wǎng)站的性能優(yōu)化 - worm128的日志 - 網(wǎng)易...

 kittywei 2011-03-03

ASP.NET夜話之二十一asp.net網(wǎng)站的性能優(yōu)化

精品c#文章 2011-01-13 22:57:46 閱讀85 評(píng)論0   字號(hào): 訂閱

本篇主要講述在ASP.NET中如何提高程序性能。提高程序性能的方法主要從編碼和數(shù)據(jù)操作及優(yōu)化配置三方面,本章要講述的知識(shí)點(diǎn)有:

l  程序編碼優(yōu)化

l  數(shù)據(jù)操作優(yōu)化

l  配置優(yōu)化

l  總結(jié)

21.1 程序編碼優(yōu)化
從編碼方面提高程序性能的方法主要涉及到集合操作、字符串連接、類型轉(zhuǎn)換等。

21.1.1 集合操作
在.NET Framework中提供了很多集合類,如ArrayList、BitArray、Hashtable、Queue、SortedList、Stack、ListDictionary、NameValueCollection、OrderedDictionary、StringCollection、List<T>及數(shù)組等,要了解各個(gè)集合的特性,選擇合適的集合。在所有的集合中數(shù)組是性能最高的,如果要存儲(chǔ)的數(shù)據(jù)類型一致和容量固定,特別是對(duì)值類型的數(shù)組進(jìn)行操作時(shí)沒有裝箱和拆箱操作,效率極高。

在選擇集合類型時(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 + "毫秒");  
 
    }  
 

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)化
在.NET Framework中String類是一個(gè)比較特殊的類,我們知道值類型變量直接在棧中分配內(nèi)存來存儲(chǔ)變量的值,并且不需要垃圾回收器來回收,大部分引用類型變量是在堆中分配內(nèi)存來存儲(chǔ)變量的值,在不再使用的情況下會(huì)被垃圾回收器回收所占用的內(nèi)存。String類型的變量雖然是引用類型變量(常用的賦值方式卻很類似于值類型變量的賦值方式,如string a=”123”),但是CLR(Common Language Runtime,通用語言運(yùn)行時(shí))通過了一種特殊的方法來存放字符串,CLR會(huì)維護(hù)一個(gè)會(huì)自動(dòng)維護(hù)一個(gè)名為“拘留池”(intern pool,不知道為什么微軟會(huì)這么叫) 的表,它包含在程序中聲明的每個(gè)唯一字符串常數(shù)的單個(gè)實(shí)例,以及以編程方式添加的 String 的任何唯一實(shí)例。該拘留池節(jié)約字符串存儲(chǔ)區(qū)。如果將字符串常數(shù)分配給幾個(gè)變量,則每個(gè)變量設(shè)置為引用“拘留池”(intern pool) 中的同一常數(shù),而不是引用具有相同值的 String 的幾個(gè)不同實(shí)例。

看如下代碼:

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;  
 
    }  
 

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)化
在開發(fā)中經(jīng)常會(huì)遇到類型轉(zhuǎn)換的問題,一種情況是由字符串類型轉(zhuǎn)換成數(shù)值類型,另一種情況是存在繼承關(guān)系或者實(shí)現(xiàn)關(guān)系的類之間進(jìn)行類型轉(zhuǎn)換。在上面的兩種轉(zhuǎn)換中如果存在不能轉(zhuǎn)換的情況,則會(huì)拋出異常,在引發(fā)和處理異常時(shí)將消耗大量的系統(tǒng)資源和執(zhí)行時(shí)間。引發(fā)異常是為了確實(shí)處理異常情況,而不是為了處理可預(yù)知的時(shí)間或控制流(這一點(diǎn)尤其要注意,不要在代碼中來使用異常進(jìn)行流程控制)。

21.1.3.1 字符串類型向值類型轉(zhuǎn)換
在.NET Framework2.0版本以前將字符串類型轉(zhuǎn)換成數(shù)值類型都是使用Parse()方法,如int.Parse("123")、char.Parse("a")及bool.Parse("TrueString")等等,如果出現(xiàn)了指定的字符串不能轉(zhuǎn)換成相應(yīng)的數(shù)值類型時(shí)就會(huì)拋出異常,可能會(huì)對(duì)性能造成不良的影響。在.NET Framework2.0及以后版本中增加了TryParse()方法,減小了性能問題。TryParse()方法使用了兩個(gè)參數(shù):第一個(gè)參數(shù)是要轉(zhuǎn)換成數(shù)值類型的字符串,第二個(gè)參數(shù)是一個(gè)帶有out關(guān)鍵字的參數(shù),并且這個(gè)方法有返回值,指示指定的字符串是否能轉(zhuǎn)換成相應(yīng)的數(shù)據(jù)類型。如果指定的字符串能轉(zhuǎn)換成相應(yīng)的數(shù)據(jù)類型則方法返回true,out參數(shù)就是指定字符串轉(zhuǎn)換成相應(yīng)數(shù)值的結(jié)果,否則方法返回false,表示不能進(jìn)行轉(zhuǎn)換而不會(huì)拋出異常。

其用法如下面的代碼所示:

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); 
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; 
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; 
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()方法
使用Server.Transfer()方法實(shí)現(xiàn)同一應(yīng)用程序下不同頁面間的重定向可以避免不必要的客戶端頁面重定向。它比Response.Redirect()方法性能要高,并且Server.Transfer()方法具有允許目標(biāo)頁從源頁中讀取控件值和公共屬性值的優(yōu)點(diǎn)。由于調(diào)用了這個(gè)方法之后瀏覽器上不會(huì)反應(yīng)更改后的頁的信息,因此它也適合以隱藏URL的形式向用戶呈現(xiàn)頁面,不過如果用戶點(diǎn)擊了瀏覽器上的“后退“按鈕或者刷新頁面有可能導(dǎo)致意外情況。

21.1.5 避免不必要的服務(wù)器往返
雖然使用服務(wù)器控件能夠節(jié)省時(shí)間和代碼,但是使用服務(wù)器控件有時(shí)間會(huì)增加頁面的往返次數(shù),如果在頁面中使用了數(shù)據(jù)綁定控件,在默認(rèn)情況下每次響應(yīng)客戶端回發(fā)而加載頁面時(shí)都會(huì)重新綁定數(shù)據(jù),其實(shí)在很多情況下這個(gè)過程是沒有必要的,使用 Page.IsPostBack 避免對(duì)往返過程執(zhí)行不必要的處理,這個(gè)在數(shù)據(jù)綁定控件那一章有所體現(xiàn)。

21.1.6 盡早釋放對(duì)象
在.NET Framework中有很多類實(shí)現(xiàn)了IDisposable接口,實(shí)現(xiàn)了IDisposable接口的類中都會(huì)有一個(gè)Dispose()方法,當(dāng)這些類的實(shí)例不再使用時(shí),應(yīng)及早調(diào)用該類的Dispose()方法以釋放所占用的資源。

21.1.7 盡量減少服務(wù)器控件的使用
服務(wù)器控件在編程中使用起來確實(shí)方便,但是這種方便是犧牲了一定的性能為前提的,比如需要在頁面某個(gè)地方顯示一個(gè)字符串,這個(gè)字符串在任何時(shí)候都不會(huì)發(fā)生變化,那么可以在HTML代碼中直接輸出,還有有些表單要實(shí)現(xiàn)點(diǎn)擊按鈕之后清空表單輸入,利用HTML中的重置按鈕就可以完成這個(gè)功能,都沒有必要使用服務(wù)器控件。

21.2 數(shù)據(jù)操作優(yōu)化
數(shù)據(jù)操作優(yōu)化方面主要是數(shù)據(jù)訪問優(yōu)化,主要有數(shù)據(jù)庫(kù)連接對(duì)象使用、數(shù)據(jù)訪問優(yōu)化、優(yōu)化SQL語句、使用緩存等。

21.2.1 數(shù)據(jù)庫(kù)連接對(duì)象使用優(yōu)化
對(duì)于數(shù)據(jù)庫(kù)連接的使用始終遵循的一條原則是:盡可能晚打開數(shù)據(jù)庫(kù)連接,盡可能早關(guān)閉數(shù)據(jù)庫(kù)連接。這個(gè)在ADO.NET一章作過講述,再次不在贅述。

除此之外,還可以使用數(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)化
如果對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)不是需要經(jīng)常讀取,可以使用相應(yīng)的DataReader對(duì)象來讀取(如SqlDataReader、OleDbDataReader或OracleDataReader),在這種情況下使用DataReader對(duì)象會(huì)得到一定的性能提升。

此外,在數(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語句
在開發(fā)中除了從C#代碼方面優(yōu)化數(shù)據(jù)訪問之外,還可以從SQL語句上優(yōu)化數(shù)據(jù)訪問。有人做過調(diào)查,在數(shù)據(jù)量大的庫(kù)中進(jìn)行數(shù)據(jù)訪問,不同的人編寫的SQL語句所花費(fèi)的時(shí)間有可能相差上百倍,因此盡量讓項(xiàng)目中對(duì)數(shù)據(jù)查詢優(yōu)化有經(jīng)驗(yàn)的人編寫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 合理使用緩存
在ASP.NET中在不同級(jí)別提供了緩存功能,比如控件級(jí)和頁面級(jí)及全局級(jí)都提供了緩存功能,在控件中或者頁面中都可以通過@ OutputCache指令來使用緩存,這對(duì)于減少一些不經(jīng)常變化并且比較耗時(shí)的操作的性能損耗很有用。

除此之外,還有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> 
<%@ 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);  
 
    }  
 

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)化
不光從程序代碼上能提高程序性能,調(diào)用某些設(shè)置也能提高程序的性能。

21.3.1 禁用調(diào)試模式
在開發(fā)過程中因?yàn)榻?jīng)常要進(jìn)行調(diào)試,所以配置將Web網(wǎng)站項(xiàng)目設(shè)置成允許調(diào)試模式,在部署網(wǎng)站時(shí)一定要禁用此模式,在運(yùn)行過程中使用調(diào)試模式將會(huì)使網(wǎng)站的性能受到很大影響。禁用調(diào)試模式是在web.config文件中設(shè)置,如下面的代碼就是禁用調(diào)試模式:

<compilation debug="false">

21.3.2 合理使用ViewState
在ASP.NET中為了維護(hù)服務(wù)器控件在HTTP請(qǐng)求之間維護(hù)其狀態(tài)啟用了服務(wù)器控件的視圖狀態(tài)。服務(wù)器控件視圖狀態(tài)為其所有屬性的累計(jì)值,這些值在后面的請(qǐng)求處理中作為變量傳遞給隱藏的字段,一般情況下這些值是經(jīng)過了一定的編碼或者加密處理之后再保存到隱藏字段中的,在后面的請(qǐng)求中再經(jīng)過反向處理得到原始的值,這些處理都是需要花費(fèi)時(shí)間的。有時(shí)候?yàn)榱颂岣邞?yīng)用程序的性能,在不需維護(hù)服務(wù)器控件的情況下可以禁用視圖狀態(tài)(默認(rèn)情況下是啟用視圖狀態(tài)的),特別是在使用數(shù)據(jù)綁定控件時(shí)一定要注意這個(gè)問題。

下面是一個(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);  
 
    }  
// --></mce:script>  
 
   
 
<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> 
<%@ 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);

    }
// --></mce:script>

 

<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ǔ)方式
ASP.NET中支持多種會(huì)話狀態(tài)數(shù)據(jù)存儲(chǔ)方式,這是一個(gè)SessionStateMode枚舉值,如下表所示:

數(shù)據(jù)存儲(chǔ)模式
 說明
 
InProc 模式
 將會(huì)話狀態(tài)數(shù)據(jù)保存在ASP.NET進(jìn)程中,只能用于單個(gè)服務(wù)器,Web服務(wù)器重其后不能保留會(huì)話狀態(tài),這是默認(rèn)的存儲(chǔ)方式
 
StateServer 模式
 將會(huì)話狀態(tài)存儲(chǔ)在單獨(dú)的進(jìn)程中,可將會(huì)話狀態(tài)用于多個(gè)Web服務(wù)器,并在Web服務(wù)器重啟后還能保留會(huì)話狀態(tài)
 
SQLServer 模式
 會(huì)話數(shù)據(jù)保存到SQL Server 數(shù)據(jù)庫(kù)中,可將會(huì)話狀態(tài)用于多個(gè)Web服務(wù)器,并在Web服務(wù)器重啟后還能保留會(huì)話狀態(tài)
 
Custom 模式
 自定義存儲(chǔ)方式
 
Off 模式
 禁用會(huì)話狀態(tài)
 

上面的每種方式都有字節(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?
<configuration> 
 
<system.web> 
 
         <sessionState mode="InProc" timeout="20"/> 
 
</system.web> 
 
</configuration> 
<configuration>

<system.web>

         <sessionState mode="InProc" timeout="20"/>

</system.web>

</configuration>

21.4 總結(jié)
上面提到的優(yōu)化方法是筆者平時(shí)做性能優(yōu)化時(shí)常用到的優(yōu)化方法,除了上面的方法之外還可以通過使用更好的算法來提高程序性能。盡管優(yōu)化的方法各異,但目的只有一個(gè):今最大可能在滿足程序要求的情況下提高性能。除了上面的方法之外還有其它的方式,希望讀者朋友在開發(fā)和學(xué)習(xí)中自己多積累這方面的經(jīng)驗(yàn)。

 

--------------------------------------------------------------------------------


說明,本篇是《ASP.NET夜話》第21章草稿,因?yàn)閷懽鲿r(shí)間是2009年12月左右,當(dāng)時(shí)還沒有出現(xiàn)ASP.NET4.0正式版和VS2010正式版,在它們出現(xiàn)之后有些地方略有些小變化。在本篇講得是從代碼和配置上提高性能,沒有講述如何使用集群、負(fù)載均衡等方法來提高性能,因?yàn)檫@超出了ASP.NET范圍之外。在這里發(fā)表這篇文章主要是周公最近要講講利用工具來優(yōu)化數(shù)據(jù)和代碼,這個(gè)只是作為引子。

此外,上面提到的方法要注意其使用場(chǎng)合。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    久久精视频免费视频观看| 免费久久一级欧美特大黄孕妇| 午夜精品黄片在线播放| 免费在线成人激情视频| 亚洲精品福利视频你懂的| 日韩视频在线观看成人| 日韩一区二区三区免费av| 果冻传媒在线观看免费高清| 欧美自拍系列精品在线| 久久热九九这里只有精品| 国产又粗又猛又爽又黄| 国产欧美日韩在线一区二区| 日本高清视频在线观看不卡| 国产精品午夜一区二区三区| 国产精品一区日韩欧美| 欧美区一区二在线播放| 日韩精品毛片视频免费看| 久久精品国产亚洲熟女| 老鸭窝老鸭窝一区二区| 九九热精彩视频在线免费 | 日本一区不卡在线观看| 99国产精品国产精品九九 | 91久久精品国产一区蜜臀| 国产超薄黑色肉色丝袜| 亚洲黄色在线观看免费高清| 亚洲欧美中文日韩综合| 国产精品欧美一区两区| 91麻豆视频国产一区二区 | 婷婷亚洲综合五月天麻豆| 日本人妻中出在线观看| 日本人妻丰满熟妇久久| 激情五月天深爱丁香婷婷| 大伊香蕉一区二区三区| 国产偷拍精品在线视频| 日本一品道在线免费观看| 国产又大又硬又粗又黄| 国产午夜精品久久福利| 黄色片一区二区三区高清| 亚洲国产av一二三区| 亚洲午夜av久久久精品| 亚洲午夜福利视频在线|