當我撰寫本專欄的時候,Microsoft® .NET Framework 2.0 和Visual Studio®2005的候選版本已經(jīng)“出爐”了,當您閱讀到本文時,它們都已經(jīng)投入使用。這一切似乎等待了很長時間。
我還記得在 2003 年的 8 月坐在 Microsoft 公司的一個房間內(nèi)傾聽 Scott Guthrie和其他人(包括我的同事 Rob Howard,他也是專欄作家)介紹 ASP.NET 2.0的大量新功能。他們演示了一個又一個功能,這些功能令我們非常吃驚,因為它們極大地簡化了 Web開發(fā),而且是以可插入和可擴展的方式實現(xiàn)的,因此在開發(fā)過程中能夠以任何所需級別進行更改。
后續(xù)測試版本中進行了大量更改,多數(shù)是以修改、錯誤修復(fù)和控件附加的形式進行的。但是,有一個功能(代碼隱藏模型)自從第一個預(yù)覽版以來已經(jīng)進行了大量更改,這主要是為了響應(yīng)客戶的反饋?,F(xiàn)在即將發(fā)布之時,我想利用這個機會描述一下這個新的代碼隱藏模型、它的基本原理,以及Web 開發(fā)人員將如何使用它。我也會介紹該模型的一些潛在的副作用以及如何在設(shè)計中解決它們。請注意,ASP.NET 2.0運行時完全支持 1.x 模型,因此針對 1.x 編寫的應(yīng)用程序可以在無需修改的情況下直接運行。
代碼隱藏
雖然該代碼隱藏模型在 2.0中是不同的,但是它的語法已經(jīng)進行了少量更改。實際上,該更改十分細微,如果您不仔細查看,甚至都無法注意到它。顯示新的代碼隱藏語法:
Default.aspx
<%@ Page Language="C#"
CodeFile="Default.aspx.cs" Inherits="MsdnMag.Default"%>
Default.aspx.cs
namespace MsdnMag
{
publicpartial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
該模型與以前的 1.x 模型有兩個區(qū)別 — 在 @ Page 指令中引入了CodeFile 屬性,以及將代碼隱藏類聲明為部分類。當開始生成該頁時,您將注意到另一個區(qū)別 —服務(wù)器端控件不再需要在代碼隱藏類中顯式聲明,但是您仍然能夠以編程方式完整地訪問它們。例如,中的窗體有若干個在代碼隱藏文件中以編程方式使用的服務(wù)器端控件,但是您可以注意到,代碼隱藏類中缺少任何顯式控件聲明。
Default.aspx
<%@ Page Language="C#"
CodeFile="Default.aspx.cs" Inherits="MsdnMag.Default"%>
<!DOCTYPE html PUBLIC "...""...">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>UntitledPage</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" OnClick="_enterButton_Click"/><br />
<asp:Label ID="_messageLabel" runat="server"/>
</div>
</form>
</body>
</html>
Default.aspx.cs
namespace MsdnMag
{
public partial class Default :System.Web.UI.Page
{
protectedvoid _enterButton_Click(object sender, EventArgs e)
{
_messageLabel.Text = "Hello there " + _nameTextBox.Text +"!";
}
}
}
其中的原因與應(yīng)用于代碼隱藏類的部分關(guān)鍵字有關(guān)。除了使用呈現(xiàn)該頁的方法將.aspx 文件轉(zhuǎn)換為一個類定義(正如它已經(jīng)做的一樣),ASP.NET現(xiàn)在也為包含受保護控件成員變量聲明的代碼隱藏類生成一個同輩部分類。然后,您的類與該生成的類定義一起編譯,并用作針對 .aspx文件生成的類的基類。結(jié)果是,您基本上以經(jīng)常使用的方式編寫代碼隱藏類,但是您不再需要聲明(或讓服務(wù)器為您聲明)服務(wù)器端控件的成員變量聲明。這一直是1.x 中一個不太穩(wěn)定的關(guān)系,因為如果您無意間修改了一個控件聲明,使得它不再與該窗體上所聲明控件的 ID匹配,就會突然停止工作?,F(xiàn)在,成員變量以隱式方式聲明并始終是正確的。顯示所涉及類集的一個示例:
Class for ASPX file generated byASP.NET
namespace ASP
{
public classdefault_aspx : MsdnMag.Default
{
...
}
}
Sibling partial class generated byASP.NET
namespace MsdnMag
{
public partial class Default :IRequiresSessionState
{
protectedTextBox _nameTextBox;
protectedButton _enterButton;
protectedLabel_messageLabel;
private HtmlForm form1;
...
}
}
Codebehind partial class that youwrite
namespace MsdnMag
{
public partial class Default : Page
{
void_enterButton_Click(object sender, EventArgs e)
{
_messageLabel.Text = "Hello there " + _nameTextBox.Text +"!";
}
}
}
請注意,該部分類模型僅當在 @ Page 指令中使用 CodeFile關(guān)鍵字時使用。如果使用不帶 CodeFile(或者帶有 src 屬性)的 Inherits 關(guān)鍵字,ASP.NET 會使用 1.x代碼隱藏類型并簡單地將類設(shè)置為 .aspx 文件的唯一基類。此外,如果您根本沒有代碼隱藏,則類生成與它在 1.x中的操作將完全相同。由于 ASP.NET 2.0 向后與 1.x 兼容,因此現(xiàn)在有大量代碼隱藏選項供您使用。
Visual Studio 2005 將使用任何 Web窗體新的部分類隱藏模型,而且如果您使用轉(zhuǎn)換向?qū)В矊⒑芎玫剞D(zhuǎn)換 Visual Studio .NET 2003項目以便使用新模型。因為 ASP.NET 2.0的一些新功能依賴于它的原因,所以如果可能,最好將所有文件轉(zhuǎn)換為新代碼隱藏模型(如果使用 VisualStudio,那么轉(zhuǎn)換幾乎是唯一的選擇,因為 Visual Studio 2005 不會打開未轉(zhuǎn)換的 1.x 項目)。例如,對Profile 屬性包的強類型訪問添加到 2.0 中代碼隱藏類的同輩部分類中,但是如果您使用 1.x代碼隱藏模型,則該強類型訪問器直接添加到 .aspx生成的類定義中,而且對于代碼隱藏類不可用。這也適用于強類型的母版頁和以前的頁訪問。
編譯
此時,您可能想知道,為什么 ASP.NET小組非要使用這個新代碼隱藏模型來使用繼承。ASP.NET 除了將來自 .aspx文件的方法呈現(xiàn)為部分類(然后這些類與簡化的代碼隱藏類合并)之外,還可以輕松生成所有控件變量聲明。這就是 Windows 窗體在.NET Framework 2.0中的工作方式。設(shè)計器生成的所有代碼被放置在同輩部分類(然后該類與您的應(yīng)用程序邏輯合并)中,事件處理程序被放置在窗體驅(qū)動的單個類中,從而在無需借助于繼承的情況下,在計算機生成的代碼和開發(fā)人員代碼之間創(chuàng)建一個完全的分離。
嗯,ASP.NET 2.0 中代碼隱藏的原始實現(xiàn)也執(zhí)行此操作 —代碼隱藏類只是一個與分析的 .aspx文件類定義合并的部分類。它簡單有效,但遺憾的是它不夠靈活。該模型的問題在于,預(yù)編譯的二進制程序集中的代碼隱藏文件不再能夠與完整的.aspx文件一起部署,因為它們現(xiàn)在必須同時編譯(使用部分類的一個限制是,一個類的所有部分必須在單個編譯中合并,而且類定義無法跨越程序集)。對于許多開發(fā)人員而言,該限制是無法接受的,因為他們已經(jīng)習慣于將二進制代碼隱藏程序集與完整的.aspx 文件一起部署,后者隨后會進行適當?shù)母露槐刂匦戮幾g。實際上,這就是默認情況下 Visual Studio .NET2003 中使用的模型,而且在實踐中非常流行。
由于重新引入了繼承模型并將部分類移到基類中,.aspx文件現(xiàn)在可以從代碼隱藏類中進行獨立部署和編譯。為此,您需要某種方式在編譯或部署過程中生成同輩的部分類,后者包含控件變量聲明,因為在過去這一直是針對請求進行的。走近ASP.NET 編譯器。
在 ASP.NET 2.0 中,ASP.NET 編譯器(aspnet_compiler.exe) 最初作為完全預(yù)編譯整個站點的一種方式引入,從而使得只部署二進制程序集成為可能(甚至也對.aspx 和 .ascx文件進行預(yù)編譯)。這是非常吸引人的,因為它消除了發(fā)出請求時的任何按需編譯,從而消除了目前在一些站點上可以看到的第一個部署后點擊。它也使得對已部署站點進行修改更加困難(因為您無法打開.aspx 文件并更改內(nèi)容),當部署只想通過標準部署過程更改的應(yīng)用程序時,這是很吸引人的。ASP.NET 2.0的發(fā)布版本提供的編譯器支持僅支持二進制的部署模型,但是它也進行了增強以支持可更新的部署模型,其中站點中的所有源代碼預(yù)編譯為二進制程序集,但是所有.aspx 和 .ascx 文件都基本保持完整,以便可以在服務(wù)器上進行更改(針對 .aspx 和 .ascx 文件的更改,涉及移除的CodeFile 屬性以及進行修改以包括程序集名的 Inherits屬性)。由于在代碼隱藏模型中重新引入了繼承,因此該模型是可能的。這樣,包含控件聲明的同輩部分類可以獨立于實際的 .aspx文件類定義生成和編譯。
圖 4 使用 aspnet_compiler.exe進行二進制部署
圖 4 顯示使用二進制部署選項對 aspnet_compiler.exe實用工具的調(diào)用,以及針對部署目錄的結(jié)果輸出。請注意,該部署目錄中的 .aspx文件只是沒有內(nèi)容的標記文件。它們之所以位于那里,是為了確保 IIS 應(yīng)用程序中 .aspx 擴展的“Check that fileexists”選項進行設(shè)置后,帶有終結(jié)點名稱的文件可用。PrecompiledApp.config文件用于跟蹤應(yīng)用程序的部署方式,以及 ASP.NET 是否需要在請求時編譯任何文件。要生成“可更新的”站點,需要將一個 -u添加到命令行,得到的 .aspx 文件將包含它們的原始內(nèi)容(而不是空的標記文件)。請注意,該功能也可以通過 Visual Studio2005 的 Build | Publish Web Site 菜單項以圖形方式訪問,如圖 5 所示。該命令行工具和 VisualStudio 都依賴于 System.Web.Compilation 命名空間的 ClientBuildManager類提供該功能。
圖 5 Visual Studio 2005 中的 Build | PublishWeb Site 工具
使用手邊的 aspnet_compiler實用工具,您無需擔心應(yīng)用程序的大體部署方式就可使其運行,因為任何站點都能以下面三種方式之一進行部署 —全源、全二進制或可更新(二進制文件中的源代碼和源代碼中的 .aspx 文件)—無需對開發(fā)中使用的頁面屬性或代碼文件進行任何更改。這在以前的 ASP.NET 版本中是不可能的,因為您必須在開發(fā)時決定是否使用 src屬性來引用代碼隱藏文件,或者預(yù)編譯它們并將程序集部署到 /bin 目錄。完整的二進制部署甚至不是一個選項。
程序集生成
既然編譯為程序集可以在三種情況下發(fā)生(由開發(fā)人員顯式進行,使用aspnet_compiler.exe,或者在請求處理中進行),因此了解文件到程序集的映射變得更為重要。實際上,根據(jù)編寫頁面的方式,您實際上可以得到一個應(yīng)用程序,在作為全源或全二進制部署時,該應(yīng)用程序可以正常工作,但在使用可更新的切換進行部署時,卻編譯失敗。
模型 ASP.NET 通常使用 App_Code 目錄內(nèi)容的單獨程序集以及global.asax 文件(如果存在),然后將每個目錄中的所有 .aspx頁編譯為單獨的程序集。(如果同一目錄中的頁面是以不同語言制作的,或者它們通過 @ Reference指令彼此依賴,則它們也可以形成單獨的程序集。)用戶控件和母版頁通常也獨立于 .aspx 頁進行編譯。例如,如果要在一個項目中包含Visual Basic® 和 C# 源代碼,也可以配置 App_Code目錄來創(chuàng)建多個程序集。在程序集創(chuàng)建的細節(jié)中有一些細微差別,這取決于您所選的部署模式。 描述特定 Web 站點的組件,該 Web站點基于您要使用的部署模式編譯為單獨的程序集。(請注意,我要忽略資源、主題和瀏覽器目錄,因為它們不包含代碼,雖然它們也編譯為單獨的程序集。正如前面提到的,目標程序集也因語言的不同和引用依賴項而異。)
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 eachuser control)
.master and associated codebehind file (separate assembly for eachmaster page)
All .aspx files and their codebehind files in agiven directory (separate assembly per directory) App_Codedirectory
global.asax
.ascx and .master files and their associated codebehind files
All .aspx files and their codebehind files in agiven 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 perdirectory) (R)
All codebehind files associated with .aspx files in a givendirectory (separate assembly per directory) (D)
When it's compiled Request time Deployment time (R) = Compiled atrequest time
(D) = Compiled at deployment time
程序集生成的另一個技巧是,使用 aspnet_compiler 的-fixednames 選項請求將每個 .aspx文件編譯為單獨的程序集,該程序集的名稱跨編譯器的不同調(diào)用保持一致。如果您想更新單個頁面而不修改部署站點上的其他程序集,這是很有用的。它也可以為任何大型站點生成大量程序集,因此您一定要在使用該選項之前測試您的部署。
如果您覺得這比較復(fù)雜,我可以告訴您它的優(yōu)點,即您無需花費大量時間考慮將哪些文件映射為單獨的程序集。.aspx文件一直在最后進行編譯,并一直包括對生成的所有其他程序集的引用,因此,無論您選擇哪種部署模型,它通常都會正常工作。
在部署中,可能實際影響您在頁面中制作代碼的方式的一個重要區(qū)別是,當使用可更新部署時編譯中的分離。當部署可更新站點時,代碼隱藏文件在部署之前編譯為單獨的程序集。從.aspx文件生成的類不進行編譯,除非作出對目錄中文件的實際請求。這與二進制部署(其中所有文件在部署之前編譯)以及源部署(其中所有文件在請求時編譯)形成了鮮明對比。以下這一簡單的示例解釋這是如何引出問題的,請考慮中帶有嵌入屬性的用戶控件(.ascx文件),以及一個使用該控件并從其代碼隱藏類設(shè)置該屬性的相關(guān)頁面。
MyUserControl.ascx
<%@ ControlLanguage="C#"%>
<script runat="server">
publicstring Color
{
get { return (string)ViewState["color"] ?? "white"; }
set { ViewState["color"] = value; }
}
protectedoverride void OnLoad(EventArgs e)
{
_mainPanel.BackColor = System.Drawing.Color.FromName(Color);
base.OnLoad(e);
}
</script>
<asp:Panel runat="server"ID="_mainPanel">
<h2>Sometext</h2>
Other text
</asp:Panel>
Default.aspx
<%@ Page Language="C#" CodeFile="Default.aspx.cs"Inherits="_Default" %>
<%@ Register Src="MyUserControl.ascx"TagName="MyUserControl" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>UntitledPage</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
{
protectedvoid Page_Load(object sender, EventArgs e)
{
MyUserControl1.Color = "purple";
}
}
上面代碼中的頁面將以源或二進制部署模式編譯并運行,但是當作為可更新站點部署時將無法編譯,原因是該用戶控件Color 屬性的定義在部署時不可用(該限制也存在于 1.x模型中)。要避免此類問題發(fā)生,通常您可以將所有代碼放在代碼隱藏文件中,或者干脆不使用代碼隱藏文件,將代碼直接放在 .aspx 和.ascx 文件中。
有關(guān)文件到程序集映射的另一個注意事項是,使用內(nèi)部關(guān)鍵字防止外部程序集訪問類中的方法,這可能只在某些部署方案中奏效而在其他方案中卻不然,這是因為存在不同的程序集映射選項。除非您提前計劃要使用哪個部署選項,否則最好避免在頁面中使用內(nèi)部方法并繼續(xù)使用類型范圍的保護關(guān)鍵字:公共、受保護和私有。
小結(jié)
對于 ASP.NET 開發(fā)人員而言,ASP.NET 2.0中的新代碼隱藏模型既熟悉又陌生。之所以說熟悉是因為,它仍然使用繼承將代碼隱藏類與其 .aspx生成的類定義相關(guān)聯(lián),而之所以說陌生是因為,諸如部分類這樣的元素和控件成員變量聲明的隱式生成都是基本的轉(zhuǎn)換。實際上,您可能不會注意到用法上的許多差別,但是無論您何時進行非一般的操作(例如,創(chuàng)建一個通用基Page 類,或者將代碼隱藏與內(nèi)聯(lián)代碼模型混合),了解本文描述的類關(guān)系和程序集映射都是很重要的。
本文來自CSDN博客,轉(zhuǎn)載請標明出處:http://blog.csdn.net/mzphp/archive/2006/04/16/665204.aspx
愛華網(wǎng)



