asp.net ajax 兩種開發模式

引言

最近花了一些時間,將微軟Asp.Net官方的Ajax視頻全部看了一遍,地址是http://www.asp.net/learn/ajax-videos/,視頻大多都很短,8至15分鐘的居多,有講述AjaxControlToolkit中控件用法的,也有講述Asp.Net Ajax常見的應用場景和技巧的。

本文介紹了使用Asp.Net Ajax做開發時兩種最常見的與服務端進行交互(客戶端請求服務端執行邏輯,服務端返回結果)的開發模式。第一種我姑且稱爲UpdatePanel模式,第二種稱爲Web Service(WCF Service)模式。

開始前的一些準備

對於這些文章,我假設大家都已經安裝好了Asp.Net Ajax Extension 和 Asp.Net Ajax Control ToolKit 這兩個組件。其中Asp.Net Ajax Extension已經包含在了.Net Framework 3.5中,而Ajax Control Toolkit可以去這個位置下載:http://www.codeplex.com/AjaxControlToolkit/Release/ProjectReleases.aspx?ReleaseId=16488 。因爲我使用的是VS2008,所以Ajax Extension無需安裝,而Ajax Control Toolkit我安裝到了GAC(Global Assembly Cache,全局程序集緩存)中,因此文章所附代碼的Bin目錄不會包含任何的dll組件。如果你想運行代碼,可以像我一樣將Ajax Control Toolkit安裝到GAC中,或者針對自己的情況(VS2005或者VS2008,私有程序集部署還是GAC部署)對代碼進行一些簡單的修改和配置。

如果你想安裝到GAC中,假設你將AjaxControlToolkit.dll拷貝到了“C:\”下,那麼可以打開“VS2008命令提示符”,然後輸入下面的命令,按回車:

gacutil -i C:\AjaxControlToolkit.dll

除此以外,還有兩點需想要說明。如果你想要在頁面的CodeBehind中使用AjaxControlToolkit中定義的類型,那麼需要在Web.config中進行一下配置,假設你和我一樣採用的是GAC部署,那麼Web.Config的設置爲:

<system.web> 
    <compilation debug="false"> 
        <assemblies> 
            <add assembly="AjaxControlToolkit, Version=3.0.20820.37372, Culture=neutral, PublicKeyToken=28F01B0E84B6D53E"/> 
            <!- 其餘略 --> 
    </compilation> 
</system.web>

在VS2008(VS2005)中,你可以將AjaxControlToolkit安裝到工具箱(Toolbox)中,但是在安裝好以後,當你向頁面拖放一個控件時,控件默認的前綴是cc1,並且會在頁面頂部自動生成一行控件的聲明,類似於這樣:

// 自動在頁面頂部產生的聲明 
<%@ Register Assembly="AjaxControlToolkit, Version=3.0.20820.37372, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" Namespace="AjaxControlToolkit" TagPrefix="cc1" %> 
// 頁面中控件的樣式 
<cc1:AutoCompleteExtender> ... </cc1:AutoCompleteExtender>

這樣讓人感覺頁面很不清爽,除此以外,cc1也沒有任何的含義。爲了解決這個問題,我們也可以在Web.Config進行一下設置:

<system.web> 
    <pages> 
        <controls> 
            <add assembly="AjaxControlToolkit, Version=3.0.20820.37372, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e" namespace="AjaxControlToolkit" tagPrefix="ajaxControlToolkit" /> 
            <!-- 其餘略 --> 
        </controls> 
    </pages> 
<system.web>

如果你和我一樣經過上面三個步驟的設置的話,那麼在Web站點Bin目錄中不會有任何的程序集,另外頁面頂部也不會再有控件的聲明,同時,拖放控件到頁面中時,它的代碼將是這樣子的:

<ajaxControlToolkit:AutoCompleteExtender> ... </ ajaxControlToolkit:AutoCompleteExtender>

本文以及所有Asp.Net Ajax相關的文章,都假設你採用了和我相同的配置。

Asp.Net Ajax - UpdatePanel模式

現在考慮一個最簡單的範例,頁面上放置一個Label控件、一個Button控件,當我們點擊Button控件的時候,將Label控件的文本更新爲當前時間,這裏的關鍵是更新時間的代碼位於服務端,而非使用Javascript在客戶端來完成。儘管這裏服務端的代碼僅僅是更新一下時間,但在實際中卻可以執行任何的服務端操作。

UpdatePanel是是大家熟悉的一種方式了,即是在頁面拖放一個UpdatePanel,將需要用Ajax方式進行更新的控件放在UpdatePanel之內,在本例中是Label控件。可以將Button控件也放置在UpdatePanel之內,也可以不放置。如果UpdatePanel內不放置Button控件,則需要設置UpdatePanel的Triggers節點,其中包括一個ControlID屬性和EventName屬性,用於指定哪個控件的哪個事件可以觸發了一個PostBack。本例中ControlID自然是Button的ID,而EventName則爲Click。也就是說當Button的Click事件觸發時,進行PostBack操作。下面是aspx頁面的主要代碼:

<asp:ScriptManager ID="ScriptManager1" runat="server"> 
</asp:ScriptManager> 
當前時間: 
<asp:UpdatePanel ID="UpdatePanel1" runat="server"> 
     <ContentTemplate> 
         <asp:Label ID="Label1" runat="server" Text="[未設置]"></asp:Label> 
     </ContentTemplate> 
    <Triggers> 
        <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" /> 
     </Triggers> 
</asp:UpdatePanel> 
<br /> 
<asp:Button ID="Button1" runat="server" οnclick="Button1_Click" Text="更新時間" />

而在後置代碼中,我們只需要像平常的Asp.Net開發一樣,編寫Button控件的Click事件處理程序就可以了:

protected void Button1_Click(object sender, EventArgs e) { 
     Label1.Text = DateTime.Now.ToLongTimeString(); 
}

OK,現在一切都已經就緒了,如果你運行這個頁面,並且點擊Button,會看到Label的值變爲了最新的時間,而且沒有因爲PostBack所產生的頁面閃動,即是人們常說的無刷新更新頁面。這可能是實現一個Asp.Net Ajax的最簡單範例了。但是它的問題是什麼呢?當我們點擊Button的時候,在服務端執行了一個完整的Asp.Net 頁面生命週期,和你不使用UpdatePanel更新頁面沒有任何的區別。可以做一個測試,在頁面在拖放一個Label,ID爲Label2,然後在Page_Load中寫入下面代碼:

protected void Page_Load(object sender, EventArgs e) { 
if (!IsPostBack) { 
        Label2.Text = DateTime.Now.ToLongTimeString(); 
    } else { 
        Label2.Text = DateTime.Now.ToLongTimeString(); 
    } 
}

然後在Page_Load一行設置斷點,接下來運行調試,會發現每次你點擊Button按鈕的時候,都會運行else{...}中的語句,說明每次頁面都會執行Page_Load方法。這說明使用這種方式時,服務器端的開銷是比較大的。這裏還可以發現一個有趣的現象,儘管服務器執行了爲Label2.Text賦值的語句,但是頁面上Label2卻並沒有更新。如果想要更新它,那麼需要將它也放置到UpdatePanel中,這裏我們可以在頁面上重新拖放一個UpdatePanel,然後把Label2放置進去。然後我們再點擊Button,會發現Label1和Label2都進行了更新。這裏又引出了一個有趣的問題:回想一下前面,我們只爲第一個UpdatePanel設置了Triggers節點,而並沒有爲後來新添的UpdatePanel設置Triggers節點,但是對一個UpdatePanel的更新也會影響到另一個。有的時候這種情況是我們所需要的,但更多時候不是,我們可能希望對於Label2的更新由其他控件的其他事件觸發。此時,可以將第二個UpdatePanel的UpdateMode屬性設爲“Conditional”,就避免了受到其他UpdatePanel提交的影響,這個值默認爲“Always”。

下面是此時Aspx頁面的代碼:

<!-- 上面相同 --> 
<hr /><br /> 
<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional"
<ContentTemplate> 
     <asp:Label ID="Label2" runat="server" Text="[未設置]"></asp:Label> 
</ContentTemplate> 
</asp:UpdatePanel>

以上這些就是一種最常見的Asp.Net Ajax開發模式了,我們看到它如何實現,也看到了它的缺陷:每次客戶端的操作,都會在服務端執行一次完整的頁面生命週期,加重了服務器的負擔,同時客戶端和服務端的通信過程中也會傳遞完整的http協議內容,增大了網絡流量。我們也應該看到它的優點:實現起來非常的簡單,操作上基本等同於通常的Asp.Net開發,所使用的控件也爲Asp.Net服務器控件(Server Control,這裏相對於HTML控件而言)

Asp.Net Ajax - Web Service模式

還有一種方式就是Web Service模式了,客戶端不再提交頁面,而只是發送Web Service請求,並對收到應答進行處理。由於這裏採用了異步方式,所以客戶端在發送WebService請求之後無需等待。採用這種方式服務端不會執行生命週期,往返的數據量也減到了最小。但缺點就是需要手動編寫一些代碼。我們來一步步看下如何完成,因爲WCF是下一代Windows平臺通信的基礎,集成了Web Service和Remoting這兩大技術,所以我們採用WCF來創建服務。

首先選擇“添加新項”,然後選擇“啓用了AJAX的WCF服務”,輸入名稱SimpleService,這樣會在站點中添加一個SimpleService.svc文件,在App_Code中創建一個SimpleService.cs,還會在Web.Config中添加相關的配置。我們只需要改動一下App_Code中的SimpleService.cs下的代碼:

public class SimpleService 

    [OperationContract] 
public string GetCurrentDate(string clientValue) { 
string rtn = "Server Time :" + DateTime.Now.ToLongTimeString() + "<br />"; 
        rtn += "Client Value(round trip): " + clientValue; 
return rtn; 
    } 
}

SimpleService還用一些特性修飾了,我將它取消掉了以節省空間。方法接受一個字符串clientValue,然後獲取服務器時間,最後返回clientValue。這段代碼看上去沒有什麼特別之處,但是注意到我在Client Value旁加了一個括號,寫着“round trip”,對於Ajax程序來說,這個值由客戶端發送,最後再返還給客戶端,進行了一趟由客戶端到服務端,再到客戶端的周遊。

爲了要讓javascript可以調用這個Web服務,我們需要在aspx頁面中對它進行註冊,拖放也一個ScriptManager到頁面上,然後向下面這樣進行設置:

<asp:ScriptManager ID="ScriptManager1" runat="server"> 
    <Services> 
        <asp:ServiceReference Path="~/SimpleService.svc" /> 
    </Services> 
</asp:ScriptManager>

接下來我們新建一個Pattern2.aspx頁面,在上面拖放三個HTML標記。一個span,一個input(Button),一個input(Text)。注意,是客戶端HTML標記,不含有runat="server"的,從這裏已經可以看到一個很大的不同,我們使用的是客戶端HTML控件。接着在input(Button)上雙擊,會自動生成Javascript腳本,此時aspx頁面的主要代碼如下:

<input id="txtSample" type="text" style="width:120px" /><br />          
<span id="spnTime">[未設置]</span><br /> 
<br /> 
<input id="Button1" type="button" value="更新時間" οnclick="return Button1_onclick()" />

最關鍵是接下來要編寫的javascript代碼,我先將它貼出來,然後再進行解釋:

<script language="javascript" type="text/javascript"> 
function Button1_onclick() { 
    var context = "Callback Values";                // 傳給回調函數 
    var clientValue = $get("txtSample").value;      // 獲得TextBox的值 
    SimpleService.GetCurrentDate(clientValue, OnComplete, OnFailed, context);   
    return true; 

function OnComplete(args, context){ 
    alert(context); 
    var span = $get("spnTime"); 
    span.innerHTML = args; 

function OnFailed(args){ 
    alert("更新日期失敗!"); 

</script>

首先看這個Button1_onclick()方法,我們先聲明瞭一個context,這個content很類似於在C#的委託變量上調用BeginInvoke()方法時的最後一個參數,這個值用於傳遞給回調函數,以方便進行一些處理。接着我們獲取了input(Text)中輸入的值,保存在了clientValue中。然後調用了Web服務,其中第一個參數就是上面定義的GetCurrentDate()時的參數,我們傳入了clientValue,第二個參數是OnComplete是成功時的回調函數,第三個是OnFailed是失敗時的回調函數,最後一個參數我們傳遞了context,可以將它交由回調函數處理。因爲是異步操作,所以沒有在這裏獲取GetCurrentDate()方法返回值,而是通過回調函數OnComplete的參數對返回值進行了傳遞。

接下來看OnComplete()函數,其實我們最需要搞清楚的就是它的兩個參數:第一個args即爲Web服務方法GetCurrentDate()的返回值;而第二個參數,即爲調用GetCurrentDate()時傳遞的最後一個參數。在方法內部,我們使用alert()顯示了context的值,隨後將Web服務的返回值顯示在了span中。OnFailed()僅僅是提示用戶更新失敗。

接下來在頁面上點擊Button,應該可以看到下面的效果:

我們再次在Page_Load的位置設置斷點,然後啓動調試,會發現當我們使用這種方式時,點擊Button服務端並沒有再次執行頁面生命週期,而參與客戶端/服務端往返的數據也是最少量的(僅往返必需數據),因此,雖然採用這種方式我們需要編寫一定量的javascript代碼,但是卻能夠顯著地提高站點的性能。

總結

這篇文章簡單的講述了使用Asp.Net Ajax進行開發時常見的兩種方式,使用UpdatePanel + 服務器控件;或者是使用 Web Service + HTML標記 + Javascript,並且這兩種方式的實現方式和效果做了簡要的說明。

感謝閱讀,希望這篇文章能給你帶來幫助!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章