asp.net|編譯
程式碼後置
雖然該程式碼後置模型在 2.0 中是不同的,但是它的文法已經進行了少量更改。實際上,該更改十分細微,如果您不仔細查看,甚至都無法注意到它
Figure 1 Syntax in ASP.NET 2.0
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="MsdnMag.Default" %>
Default.aspx.cs
namespace MsdnMag
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
圖 1 顯示新的程式碼後置文法。
該模型與以前的 1.x 模型有兩個區別 — 在 @ Page 指令中引入了 CodeFile 屬性,以及將程式碼後置類別聲明為部分類。當開始產生該頁時,您將注意到另一個區別 — 伺服器端控制項不再需要在程式碼後置類別中顯式聲明,但是您仍然能夠以編程方式完整地訪問它們。例如,
Figure 2 Implicit Server-Side Control Access
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="MsdnMag.Default" %>
<!DOCTYPE html PUBLIC "..." "...">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Enter your name:
<asp:TextBox ID="_nameTextBox" runat="server" /><br />
<asp:Button ID="_enterButton" runat="server"
Text="Enter" /> <br />
<asp:Label ID="_messageLabel" runat="server" />
</div>
</form>
</body>
</html>
Default.aspx.cs
namespace MsdnMag
{
public partial class Default : System.Web.UI.Page
{
protected void _enterButton_Click(object sender, EventArgs e)
{
_messageLabel.Text = "Hello there " + _nameTextBox.Text + "!";
}
}
}
圖 2 中的表單有若干個在程式碼後置檔案中以編程方式使用的伺服器端控制項,但是您可以注意到,程式碼後置類別中缺少任何顯式控制項聲明。
其中的原因與應用於程式碼後置類別的部分關鍵字有關。除了使用呈現該頁的方法將 .aspx 檔案轉換為一個類定義(正如它已經做的一樣),ASP.NET 現在也為包含受保護控制項成員變數聲明的程式碼後置類別產生一個同輩部分類。然後,您的類與該產生的類定義一起編譯,並用作針對 .aspx 檔案產生的類的基類。結果是,您基本上以經常使用的方式編寫程式碼後置類別,但是您不再需要聲明(或讓伺服器為您聲明)伺服器端控制項的成員變數聲明。這一直是 1.x 中一個不太穩定的關係,因為如果您無意間修改了一個控制項聲明,使得它不再與該表單上所聲明控制項的 ID 匹配,就會突然停止工作。現在,成員變數以隱式方式聲明並始終是正確的。
Figure 3 Class Generation with Codebehind
Class for ASPX file generated by ASP.NET
namespace ASP
{
public class default_aspx : MsdnMag.Default
{
...
}
}
Sibling partial class generated by ASP.NET
namespace MsdnMag
{
public partial class Default : IRequiresSessionState
{
protected TextBox _nameTextBox;
protected Button _enterButton;
protected Label _messageLabel;
private HtmlForm form1;
...
}
}
Codebehind partial class that you write
namespace MsdnMag
{
public partial class Default : Page
{
void _enterButton_Click(object sender, EventArgs e)
{
_messageLabel.Text = "Hello there " + _nameTextBox.Text + "!";
}
}
}
圖 3 顯示所涉及類集的一個樣本。
請注意,該部分類模型僅當在 @ Page 指令中使用 CodeFile 關鍵字時使用。如果使用不帶 CodeFile(或者帶有 src 屬性)的 Inherits 關鍵字,ASP.NET 會使用 1.x 程式碼後置類別型並簡單地將類設定為 .aspx 檔案的唯一基類。此外,如果您根本沒有程式碼後置,則類產生與它在 1.x 中的操作將完全相同。由於 ASP.NET 2.0 向後與 1.x 相容,因此現在有大量程式碼後置選項供您使用。
Visual Studio 2005 將使用任何 Web Form新的部分類隱藏模型,而且如果您使用轉換嚮導,它也將很好地轉換 Visual Studio .NET 2003 項目以便使用新模型。因為 ASP.NET 2.0 的一些新功能依賴於它的原因,所以如果可能,最好將所有檔案轉換為新程式碼後置模型(如果使用 Visual Studio,那麼轉換幾乎是唯一的選擇,因為 Visual Studio 2005 不會開啟未轉換的 1.x 項目)。例如,對 Profile 屬性包的強型別訪問添加到 2.0 中程式碼後置類別的同輩部分類中,但是如果您使用 1.x 程式碼後置模型,則該強型別訪問器直接添加到 .aspx 產生的類定義中,而且對於程式碼後置類別不可用。這也適用於強型別的主版頁面和以前的頁訪問。
返回頁首
編譯
此時,您可能想知道,為什麼 ASP.NET 小組非要使用這個新程式碼後置模型來使用繼承。ASP.NET 除了將來自 .aspx 檔案的方法呈現為部分類(然後這些類與簡化的程式碼後置類別合并)之外,還可以輕鬆產生所有控制項變數聲明。這就是 Windows 表單在 .NET Framework 2.0 中的工作方式。設計器產生的所有代碼被放置在同輩部分類(然後該類與您的應用程式邏輯合并)中,事件處理常式被放置在表單驅動的單個類中,從而在無需藉助於繼承的情況下,在電腦產生的程式碼和開發人員代碼之間建立一個完全的分離。
嗯,ASP.NET 2.0 中程式碼後置的原始實現也執行此操作 — 程式碼後置類別只是一個與分析的 .aspx 檔案類定義合并的部分類。它簡單有效,但遺憾的是它不夠靈活。該模型的問題在於,先行編譯的二進位程式集中的程式碼後置檔案不再能夠與完整的 .aspx 檔案一起部署,因為它們現在必須同時編譯(使用部分類的一個限制是,一個類的所有部分必須在單個編譯中合并,而且類定義無法跨越程式集)。對於許多開發人員而言,該限制是無法接受的,因為他們已經習慣於將二進位程式碼後置程式集與完整的 .aspx 檔案一起部署,後者隨後會進行適當的更新而不必重新編譯。實際上,這就是預設情況下 Visual Studio .NET 2003 中使用的模型,而且在實踐中非常流行。
由於重新引入了繼承模型並將部分類移到基類中,.aspx 檔案現在可以從程式碼後置類別中進行獨立部署和編譯。為此,您需要某種方式在編譯或部署過程中產生同輩的部分類,後者包含控制項變數聲明,因為在過去這一直是針對請求進行的。走近 ASP.NET 編譯器。
在 ASP.NET 2.0 中,ASP.NET 編譯器 (aspnet_compiler.exe) 最初作為完全先行編譯整個網站的一種方式引入,從而使得只部署二進位程式整合為可能(甚至也對 .aspx 和 .ascx 檔案進行先行編譯)。這是非常迷人的,因為它消除了發出請求時的任何按需編譯,從而消除了目前在一些網站上可以看到的第一個部署後點擊。它也使得對已部署網站進行修改更加困難(因為您無法開啟 .aspx 檔案並更改內容),當部署只想通過標準部署過程更改的應用程式時,這是很迷人的。ASP.NET 2.0 的發布版本提供的編譯器支援僅支援二進位的部署模型,但是它也進行了增強以支援可更新的部署模型,其中網站中的所有原始碼先行編譯為二進位程式集,但是所有 .aspx 和 .ascx 檔案都基本保持完整,以便可以在伺服器上變更(針對 .aspx 和 .ascx 檔案的更改,涉及移除的 CodeFile 屬性以及進行修改以包括程式集名的 Inherits 屬性)。由於在程式碼後置模型中重新引入了繼承,因此該模型是可能的。這樣,包含控制項聲明的同輩部分類可以獨立於實際的 .aspx 檔案類定義產生和編譯。
圖 4 使用 aspnet_compiler.exe 進行二進位部署
圖 4 顯示使用二進位部署選項對 aspnet_compiler.exe 工具 + 生產力的調用,以及針對部署目錄的結果輸出。請注意,該部署目錄中的 .aspx 檔案只是沒有內容的標記檔案。它們之所以位於那裡,是為了確保 IIS 應用程式中 .aspx 擴充的“Check that file exists”選項進行設定後,帶有終結點名稱的檔案可用。PrecompiledApp.config 檔案用於跟蹤應用程式的部署方式,以及 ASP.NET 是否需要在請求時編譯任何檔案。要產生“可更新的”網站,需要將一個 -u 添加到命令列,得到的 .aspx 檔案將包含它們的原始內容(而不是空的標記檔案)。請注意,該功能也可以通過 Visual Studio 2005 的 Build | Publish Web Site 功能表項目以圖形方式訪問,如圖 5 所示。該命令列工具和 Visual Studio 都依賴於 System.Web.Compilation 命名空間的 ClientBuildManager 類提供該功能。
圖 5 Visual Studio 2005 中的 Build | Publish Web Site 工具
使用手邊的 aspnet_compiler 工具 + 生產力,您無需擔心應用程式的大體部署方式就可使其運行,因為任何網站都能以下面三種方式之一進行部署 — 全源、全二進位或可更新(二進位檔案中的原始碼和原始碼中的 .aspx 檔案)— 無需對開發中使用的頁面屬性或代碼檔案進行任何更改。這在以前的 ASP.NET 版本中是不可能的,因為您必須在開發時決定是否使用 src 屬性來引用程式碼後置檔案,或者先行編譯它們並將程式集部署到 /bin 目錄。完整的二進位部署甚至不是一個選項。
返回頁首
程式集產生
既然編譯為程式集可以在三種情況下發生(由開發人員顯式進行,使用 aspnet_compiler.exe,或者在請求處理中進行),因此瞭解檔案到程式集的映射變得更為重要。實際上,根據編寫頁面的方式,您實際上可以得到一個應用程式,在作為全源或全二進位部署時,該應用程式可以正常工作,但在使用可更新的切換進行部署時,卻編譯失敗。
模型 ASP.NET 通常使用 App_Code 目錄內容的單獨程式集以及 global.asax 檔案(如果存在),然後將每個目錄中的所有 .aspx 頁編譯為單獨的程式集。(如果同一目錄中的頁面是以不同語言製作的,或者它們通過 @ Reference 指令彼此依賴,則它們也可以形成單獨的程式集。)使用者控制項和主版頁面通常也獨立於 .aspx 頁進行編譯。例如,如果要在一個項目中包含 Visual Basic® 和 C# 原始碼,也可以配置 App_Code 目錄來建立多個程式集。在程式集建立的細節中有一些細微差別,這取決於您所選的部署模式
Figure 6 Assembly Generation
Deployment Mode
All Source All Binary Updatable (mixed)
What compiles into a unique assembly App_Code directory
global.asax
.ascx and associated codebehind file (separate assembly for each user control)
.master and associated codebehind file (separate assembly for each master page)
All .aspx files and their codebehind files in a given directory (separate assembly per directory) App_Code directory
global.asax
.ascx and .master files and their associated codebehind files
All .aspx files and their codebehind files in a given directory (separate assembly per directory) App_Code directory (D)
global.asax (R)
.ascx and .master files (R)
codebehind files for .ascx and .master files (D)
All .aspx files in a given directory (separate assembly per directory) (R)
All codebehind files associated with .aspx files in a given directory (separate assembly per directory) (D)
When it's compiled Request time Deployment time (R) = Compiled at request time
(D) = Compiled at deployment time
圖 6 描述特定 Web 網站的組件,該 Web 網站基於您要使用的部署模式編譯為單獨的程式集。(請注意,我要忽略資源、主題和瀏覽器目錄,因為它們不包含代碼,雖然它們也編譯為單獨的程式集。正如前面提到的,目標程式集也因語言的不同和引用依賴項而異。)
程式集產生的另一個技巧是,使用 aspnet_compiler 的 -fixednames 選項請求將每個 .aspx 檔案編譯為單獨的程式集,該程式集的名稱跨編譯器的不同調用保持一致。如果您想更新單個頁面而不修改部署網站上的其他程式集,這是很有用的。它也可以為任何大型網站產生大配量序集,因此您一定要在使用該選項之前測試您的部署。
如果您覺得這比較複雜,我可以告訴您它的優點,即您無需花費大量時間考慮將哪些檔案對應為單獨的程式集。.aspx 檔案一直在最後進行編譯,並一直包括對產生的所有其他程式集的引用,因此,無論您選擇哪種部署模型,它通常都會正常工作。
在部署中,可能實際影響您在頁面中製作代碼的方式的一個重要區別是,當使用可更新部署時編譯中的分離。當部署可更新網站時,程式碼後置檔案在部署之前編譯為單獨的程式集。從 .aspx 檔案產生的類不進行編譯,除非作出對目錄中檔案的實際請求。這與二進位部署(其中所有檔案在部署之前編譯)以及源部署(其中所有檔案在請求時編譯)形成了鮮明對比。以下這一簡單的樣本解釋這是如何引出問題的,
Figure 7 User Control
MyUserControl.ascx
<%@ Control Language="C#"%>
<script runat="server">
public string Color
{
get { return (string)ViewState["color"] ?? "white"; }
set { ViewState["color"] = value; }
}
protected override void OnLoad(EventArgs e)
{
_mainPanel.BackColor = System.Drawing.Color.FromName(Color);
base.OnLoad(e);
}
</script>
<asp:Panel runat="server" ID="_mainPanel">
<h2>Some text</h2>
Other text
</asp:Panel>
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Src="MyUserControl.ascx" TagName="MyUserControl" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"' target=_blank>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="' target=_blank>http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<uc1:MyUserControl id="MyUserControl1" runat="server">
</uc1:MyUserControl></div>
</form>
</body>
</html>
Default.aspx.cs
using System;
using System.Web.UI;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
MyUserControl1.Color = "purple";
}
}
請考慮圖 7 中帶有嵌入屬性的使用者控制項(.ascx 檔案),以及一個使用該控制項並從其程式碼後置類別設定該屬性的相關頁面。
圖 7 中的頁面將以源或二進位部署模式編譯並運行,但是當作為可更新網站部署時將無法編譯,原因是該使用者控制項 Color 屬性的定義在部署時不可用(該限制也存在於 1.x 模型中)。要避免此類問題發生,通常您可以將所有代碼放在程式碼後置檔案中,或者乾脆不使用程式碼後置檔案,將代碼直接放在 .aspx 和 .ascx 檔案中。
有關檔案到程式集映射的另一個注意事項是,使用內部關鍵字防止外部程式集訪問類中的方法,這可能只在某些部署方案中奏效而在其他方案中卻不然,這是因為存在不同的程式集映射選項。除非您提前計劃要使用哪個部署選項,否則最好避免在頁面中使用內部方法並繼續使用類型範圍的保護關鍵字:公用、受保護和私人。
返回頁首
小結
對於 ASP.NET 開發人員而言,ASP.NET 2.0 中的新程式碼後置模型既熟悉又陌生。之所以說熟悉是因為,它仍然使用繼承將程式碼後置類別與其 .aspx 產生的類定義相關聯,而之所以說陌生是因為,諸如部分類這樣的元素和控制項成員變數聲明的隱式產生都是基本的轉換。實際上,您可能不會注意到用法上的許多差別,但是無論您何時進行非一般的操作(例如,建立一個通用基 Page 類,或者將程式碼後置與內聯代碼模型混合),瞭解本文描述的類別關係和程式集映射都是很重要的。