ASP.Net中程式構架與程式碼的分離

來源:互聯網
上載者:User
一年前,當本人拿到一個名叫TWIG的PHP程式時,立即被作者OOP編程思想所折服,很難想像TWIG中所有的功能(行事曆、郵件、個人化)均在一個PHP檔案(index.php3)中執行完成,這就得益於作者採用了程式碼與頁面構架分離的思想,但是我也看到儘管作者做了很大的努力,但由於PHP的局限性,程式並沒有真正做到代碼與構架的分離,index.php3這個主檔案由於要執行的功能太多,所以其require的模組檔案相當之多,至使整個檔案依然顯得十分零亂,本人愚昧,當時花了半個月的時間,才真正明白程式的構架,分析代碼之苦,無人能知啊(黯然淚下......)。

TWIG程式對我此後的編程有著很大的影響,但是即使這樣的作品,依然沒有擺脫程式碼與HTML代碼混雜的局面。

程式碼與頁面構架的分離是WEB程式員多年的夢想。在ASP.Net出現之前,無論是ASP、PHP還是JSP,程式碼與HTML代碼都是混雜在一起的,這種做法,雖然在WEB技術初期受到讚揚,但是隨著時間的的推移,它的弊端是越來越明顯,當程式碼很長時,HTML代碼與其混雜,程式的可讀性變得很差,讓人無法分清程式真正要表示的頁面構架。

而新技術ASP.Net則通過Codebehind、使用者控制項(User Control)以及自訂控制項(Custom Control)等方法真正做到了代碼的分離。這是一個了不起的進步,大家可以在本文中看到分離代碼後的ASP.Net程式的結構是多麼的清晰。

為了便於理解,這裡設計的頁面比較簡單,頁面分為三個主要的部分,頭部包含一個AdRotator控制項(用於顯示廣告)與一個Label控制項(用於顯示當前廣告連結地址);中部是一個登陸頁面,包括兩個TextBox控制項(分別用於輸入使用者名稱與密碼)、一個Label控制項(顯示登陸是否成功)與一個Button控制項(作為提交按鈕);底部包含兩個Label控制項(分別顯示目前使用者名與使用者權限)。

熟悉ASP.Net的朋友,馬上就會意識到頭部由於使用了AdRotator控制項,所以必定存在OnAdCreated事件以便在Label控制項顯示相應連結;而中部由於使用Button控制項做為提交按鈕,所以必定有一個OnClick事件處理。

1 CodeBehind

首先我們就看看如何使用CodeBehind方法來實現代碼與頁面構架的分離,下面給出的來源程式是主ASP.Net程式--Example1.aspx:

<% @ Page Src="cs\EventHandle.cs" Inherits="Aspcn" %>
<html>
<head>
<title></title>
</head>
<body>
<form runat="server">
<asp:Panel id="Header" runat="server">
<asp:AdRotator id="ad" AdvertisementFile="AdBanners\ad.xml" BorderWidth="0" OnAdCreated="AdCreated" runat="server" /><br>
當前廣告連結:<asp:Label id="lblAdText" ForeColor="red" runat="server" />
</asp:Panel>

<asp:Panel id="Logon" runat="server">
<table>
<tr><td colspan="2" align="center"><b>登陸視窗</b></td></tr>
<tr><td colspan="2" align="center"><asp:Label id="lblMsgShow" ForeColor="red" runat="server" /></td></tr>
<tr><td>使用者名稱:</td><td><asp:TextBox id="tbUserName" runat="server" /></td></tr>
<tr><td>密碼:</td><td><asp:TextBox id="tbPasswd" TextMode="Password" runat="server" /></td></tr>
<tr><td><asp:Button id="btnSubmit" Text="登陸" OnClick="Submit_Click" runat="server" /></td></tr>
</table>
</asp:Panel>

<asp:Panel id="Footer" runat="server">
使用者名稱:<asp:Label id="lblUserName" Font-Name="Arial" ForeColor="red" Text="遊客" runat="server" />

許可權:<asp:Label id="lblPurview" Font-Name="Arial" Text="無" ForeColor="red" runat="server" />
</asp:Panel>
</form>
</body>
</html>

常式中,大家可以清楚地看到程式中不包含任何C#、VB、JavaScript來處理OnAdCreated與OnClick事件,但是執行本程式,程式能夠正常使用(2-1與圖2-2)。這便是使用CodeBehinde的結果,事件處理已經被轉移到其它程式中定義執行。請大家注意本例中第一行的資訊:

<% @ Page Src="cs\EventHandle.cs" Inherits="Aspcn" %>

一般在ASP.Net程式中,Page指令都在設定本程式應當使用什麼語言(使用Language屬性),而本例中沒有出現Language屬性,而是出現了兩個新的Page屬性:Src與Inherits。Src屬性設定事件處理真正的代碼位置,Inherits屬性則設定需要引入的類名。可以看到本例中定義事件處理的檔案是EventHandle.cs,我們來看看它的具體內容: using System;
using System.Data;
using System.Data.SqlClient;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
public class Aspcn : Page
{
//聲明Web Form中出現的控制項
public Label lblAdText,lblUserName,lblPurview,lblMsgShow;
public TextBox tbUserName,tbPasswd;
public Button btnSubmit;
public AdRotator ad;

private string strConnString = "server=(local)\\Feidao;database=aspcn;Trusted_Connection=yes";

//處理Adrotator控制項建立事件
public void AdCreated(Object src,AdCreatedEventArgs e)
{
lblAdText.Text = e.AlternateText;
}

public void Submit_Click(Object sender,EventArgs e)
{
SqlConnection MyConn = new SqlConnection(strConnString);
MyConn.Open();
string strUserName,strPassword,strSelect;
strUserName = tbUserName.Text;
strPassword = tbPasswd.Text;
strSelect = "select * from bbs_user where id='"+strUserName+"' and password='"+strPassword+"'";
SqlCommand MyComm = new SqlCommand(strSelect,MyConn);
SqlDataReader dr = MyComm.ExecuteReader();
if(dr.Read())
{
//登陸成功
lblMsgShow.Text = "登陸成功";
lblUserName.Text = dr["id"].ToString();
lblPurview.Text = dr["purview"].ToString();
}
else
{
//登陸不成功
lblMsgShow.Text = "登陸不成功";
}
dr.Close();
MyConn.Close();
}
}

進行事件處理是定義在一個類中的(本例中是Aspcn,注意大小寫),由於需要與Web Forms相關聯,所以此類還必須繼承Page類。

剖析器,大家可以看到程式中對事件的處理操作是與普通的未進行代碼分離的程式是一樣的,並沒有什麼特別的地方。(本人在程式中已經給出的相關注釋,相信對大家理解程式有所協助)

使用CodeBehind技術後,大家需要多寫一些代碼,比如聲明控制項等,也許大家很不喜歡多寫這樣的代碼,但是大家也必須看到使用了CodeBehind技術後,主程式的可讀性大大增加了。在Example1.aspx中相信大家很快就可以區分頁面構架的各個部分,大家想想這些構架如果在其它技術是否能看得如此清楚?
(這裡的程式只做示範用,呵呵,大家可不要抓我什麼引號漏洞這些小辮子喲)

2 使用者控制項(UserControl)
CodeBehind技術真正實現了代碼與構架的分離,比以前的技術前進了一大步,但是它的缺陷也是顯而易見的,比如首頁面中部那個登陸區,如果內容很多,HTML顯示代碼的依然會佔用很大的地區,程式的可讀性依然會降低。

ASP.Net也提供瞭解決辦法,這就是使用者控制項。

使用者控制項我們可以將其視為不用編譯的Server控制項。即然是控制項,那麼就肯定會遵從控制項的使用方法。我們將Example1.aspx中的每個Panel整體看成為一個控制項,因此Example1.aspx的主體部分通過使用使用者控制項便可以減少為只有三行:

<% @ Register TagPrefix="aspcn" TagName="Header" Src="UserControls/Header.ascx" %>
<% @ Register TagPrefix="aspcn" TagName="Logon" Src="UserControls/Logon.ascx" %>
<% @ Register TagPrefix="aspcn" TagName="Footer" Src="UserControls/Footer.ascx" %>
<html>
<head>
<title></title>
</head>
<body>
<form runat="server">
<aspcn:Header id="MyHeader" runat="server" />
<aspcn:Logon id="MyLogon" runat="server" />
<aspcn:Footer id="MyFooter" runat="server" />
</form>
</body>
</html>

執行這個程式,其運行結果與使用CodeBehind技術的結果是一樣的,但是現在的ASP.Net程式更加容易區分頁面構架了。

<aspcn:Header id="MyHeader" runat="server" />
<aspcn:Logon id="MyLogon" runat="server" />
<aspcn:Footer id="MyFooter" runat="server" />

這三行代碼,使用了三個使用者控制項,這麼少的代碼大家一眼就可以清楚的看到頁面被分為三個部分。

要使用使用者控制項就必須使用Register指令,TagPrefix屬性定義是的一個Namespace的名字,以保證它在這個頁面的唯一性;TagName屬性是在定義一個類(class)的別名,由於使用者控制項執行時是被CLR編譯成為類來執行的,所以就必須給本程式中每個使用者控制項一個唯一的名字,以便於大家區分;Src屬性則是具體指出了使用的使用者控制項的檔案名稱(使用者控制項均以.ascx結尾)。

使用者控制項的使用與普通Server控制項一樣:
<namespace:class ... runat="server" />
namespace表示定義的命名空間,class則是相應的類名,具體的使用例子有:
<aspcn:Logon id="MyLogon" runat="server" />

下面是使用者控制項顯示程式中所使用的使用者控制項的具體內容:

Header.ascx(Header使用者控制項)

<Script Language="C#" Runat="Server">
private void AdCreated(Object src,AdCreatedEventArgs e)
{
lblAdText.Text = e.AlternateText;
}
</script>
<asp:AdRotator id="ad" AdvertisementFile="..\AdBanners\ad.xml" BorderWidth="0" OnAdCreated="AdCreated" runat="server" /><br>
當前廣告連結:<asp:Label id="lblAdText" ForeColor="red" runat="server" />

Logon.ascx(Logon使用者控制項)

<% @ Import Namespace="System.Data" %>
<% @ Import Namespace="System.Data.SqlClient" %>
<Script Language="C#" Runat="Server">
protected string strConnString = "server=(local)\\Feidao;database=aspcn;Trusted_Connection=yes";
//定義UserControl的屬性
public string UserName
{
get
{
return tbUserName.Text;
}
set
{
tbUserName.Text = value;
}
}
public string Password
{
get
{
return tbPasswd.Text;
}
set
{
tbPasswd.Text = value;
}

}

//事件處理
private void Submit_Click(Object sender,EventArgs e)
{
SqlConnection MyConn = new SqlConnection(strConnString);
MyConn.Open();
string strUserName,strPassword,strSelect;
strUserName = tbUserName.Text;
strPassword = tbPasswd.Text;
strSelect = "select * from bbs_user where id='"+strUserName+"' and password='"+strPassword+"'";
SqlCommand MyComm = new SqlCommand(strSelect,MyConn);
SqlDataReader dr = MyComm.ExecuteReader();
if(dr.Read())
{
//登陸成功
lblMsgShow.Text = "登陸成功";
Session["UserName"] = dr["id"].ToString();
Session["Purview"] = dr["purview"].ToString();
}
else
{
//登陸不成功
lblMsgShow.Text = "登陸不成功";
}
dr.Close();
MyConn.Close();
}
</script>
<table>
<tr><td colspan="2" align="center"><b>登陸視窗</b></td></tr>
<tr><td colspan="2" align="center"><asp:Label id="lblMsgShow" ForeColor="red" runat="server" /></td></tr>
<tr><td>使用者名稱:</td><td><asp:TextBox id="tbUserName" runat="server" /></td></tr>
<tr><td>密碼:</td><td><asp:TextBox id="tbPasswd" TextMode="Password" runat="server" /></td></tr>
<tr><td><asp:Button id="btnSubmit" Text="登陸" OnClick="Submit_Click" runat="server" /></td></tr>
</table>

Footer.ascx(Footer使用者控制項)

<Script Language="C#" Runat="Server">
private void Page_Load(Object src,EventArgs e)
{
if(Session["UserName"]!=null)
{
lblUserName.Text = (string)Session["UserName"];
lblPurview.Text = (string)Session["Purview"];
}
}
</script>
使用者名稱:<asp:Label id="lblUserName" Font-Name="Arial" ForeColor="red" Text="遊客" runat="server" />

許可權:<asp:Label id="lblPurview" Font-Name="Arial" Text="無" ForeColor="red" runat="server" />

每個控制項包含有自已的顯示代碼以及相應的程式碼。

我們可以將一些常用的功能製作成為固定的使用者控制項,當需要時,我們就可直接拿來使用,而不需要使用煩人的Crtl+C,Ctrl+V來"複製"、"粘貼"長長的一大堆代碼。

使用者控制項不僅做到了程式碼與頁面構架的分離,而且還增加了代碼重用性。

3 自訂控制項(Custom Control)

使用者控制項是很不錯的選擇,但是由於每個使用者控制項都是一個ascx檔案,當這些控制項很多時,它們的使用就顯得比較零亂。此時我們便想可不可以將一些比較相似的控制項整合起來,在程式中只需要引用一次,便全部搞定。這是個很不錯的想法,我們把這個想法說得更加專業一些:"將多個類(class)匯入同一個命名空間(namespace)"。呵呵,怎麼樣,這句話是不是有點有耳熟?大家快去查一查Server控制項的定義,是不是發現這句話是...

我們下面就要接觸如何寫Server控制項。編寫Server控制項並不是一件輕鬆容易的事情,需要對.Net平台有比較深的瞭解,適合於進階使用者,因此這裡本人也不會具體描述Server控制項的編寫步驟(要細細講這個,非得寫書不可)。請大家比較一下自訂控制項原始碼與使用者控制項的區別,作一些大致的瞭解:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace aspcn
{
//首先是Header
public class Header:Control,INamingContainer
{
private AdRotator ad;
private Label lblAdText;

protected override void CreateChildControls()
{
//加入AdRotator廣告控制項
ad = new AdRotator();
ad.AdvertisementFile = "AdBanners/ad.xml";
ad.BorderWidth = 0;
ad.AdCreated += new AdCreatedEventHandler(this.OnAdCreated);
this.Controls.Add(ad);

this.Controls.Add(new LiteralControl("<br>"+"當前廣告連結為:"));
//加入Label控制項
lblAdText = new Label();
lblAdText.ForeColor = Color.Red;
this.Controls.Add(lblAdText);
}
private void OnAdCreated(Object sender,AdCreatedEventArgs e)
{
this.lblAdText.Text = e.AlternateText;
}
}
//接著是Logon
public class Logon : Control,INamingContainer
{
private string strConnString = "server=(local)\\Feidao;database=aspcn;Trusted_Connection=yes";
private Label lblMsgShow;
private TextBox tbUserName,tbPasswd;
public String UserName
{
get
{
return tbUserName.Text;
}
set
{
tbUserName.Text = value;
}
}

protected override void CreateChildControls()
{
//添加HTML標籤
this.Controls.Add(new LiteralControl("<table><tr><td colspan=\"2\" align=\"center\"><b>登陸視窗</b></td></tr> <tr><td colspan=\"2\" align=\"center\">"));
//添加MsgShow Label控制項
lblMsgShow = new Label();
lblMsgShow.ForeColor = Color.Red;
this.Controls.Add(lblMsgShow);
this.Controls.Add(new LiteralControl("</td></tr><tr><td>使用者名稱:</td><td>"));
//添加UserName與Passwd TextBox控制項
tbUserName = new TextBox();
this.Controls.Add(tbUserName);
this.Controls.Add(new LiteralControl("</td></tr><tr><td>密碼:</td><td>"));
tbPasswd = new TextBox();
tbPasswd.TextMode = TextBoxMode.Password;
this.Controls.Add(tbPasswd);
this.Controls.Add(new LiteralControl("</td></tr><tr><td>"));
//添加BtnSubmit Button控制項
Button btnSubmit = new Button();
btnSubmit.Text = "登陸";
btnSubmit.Click += new EventHandler(this.Submit_Click);
this.Controls.Add(btnSubmit);
this.Controls.Add(new LiteralControl("</td></tr></table>"));
}
//顯示完畢
private void Submit_Click(Object sender,EventArgs e)
{
SqlConnection MyConn = new SqlConnection(strConnString);
MyConn.Open();
string strUserName,strPassword,strSelect;
strUserName = tbUserName.Text;
strPassword = tbPasswd.Text;
strSelect = "select * from bbs_user where id='"+strUserName+"' and password='"+strPassword+"'";
SqlCommand MyComm = new SqlCommand(strSelect,MyConn);
SqlDataReader dr = MyComm.ExecuteReader();
if(dr.Read())
{
//登陸成功
this.lblMsgShow.Text = "登陸成功";
}
else
{
//登陸不成功
this.lblMsgShow.Text = "登陸不成功";
}
dr.Close();
MyConn.Close();
}
}
//最後是Footer
public class Footer : Control,INamingContainer
{
private string _UserName,_Purview;

public string UserName
{
get
{
return _UserName;
}
set
{
_UserName = value;
}
}

public string Purview
{
get
{
return _Purview;
}
set
{
_Purview = value;
}
}

public Footer()
{
_UserName = "遊客";
_Purview = "無";
}

protected override void CreateChildControls()
{
this.Controls.Add(new LiteralControl("使用者名稱:"));
Label lblUserName = new Label();
lblUserName.ForeColor = Color.Red;
lblUserName.Font.Name = "Arial";
lblUserName.Text = this.UserName;
this.Controls.Add(lblUserName);
//this.Controls.Add(new LiteralControl("nbsp;"));

this.Controls.Add(new LiteralControl("許可權:"));
Label lblPurview = new Label();
lblPurview.ForeColor = Color.Red;
lblPurview.Font.Name = "Arial";
lblPurview.Text = this.Purview;
this.Controls.Add(lblPurview);
}
}
}

上面和程式是將需要實現的功能,全部匯入了自訂控制項。程式中可以看到,在aspcn命名空間中包含三個類(Header,Logon,Footer),這三個類正是構架三個主體部分。

要使用自訂控制項,還必須將原代碼進行編譯。

csc /t:library /out:aspcn.dll /r:System.Data.dll,System.Web.dll,System.Drawing.dll CustomControls.cs

C#程式編譯指令的用法,本人在此也不再重複。需要注意的是編譯的檔案名稱,必須與控制項中namespace的名字一致。

編譯後的dll,仍然不能使用,我們必須將其放到.Net平台中最著名的目錄--/bin中,bin目錄(如果不存在,可以自行建立)存放的是當前虛擬目錄中所有使用自訂控制項以及組件,CLR在執行ASP.Net程式時會自動搜尋此目錄中的檔案,以找到與ASP.Net程式相匹配的Namespace、Class 以及Assembly。

當我們將程式編譯好的aspcn.dll放入/bin目錄後,這個自己編寫的Server控制項便可以使用了。
(需要聲明一下,由於編寫Server控制項時不能使用Session等變數,以至無法做到兩個class之間的通訊,因此在預設狀態下Footer控制項並不能像前面的程式一樣隨Session內容發生改變,不過可以通過普通操作Server控制項的方法來操作相應的屬性達到相同的效果,此處為節約版面,未採用)
下面再來看看主體Web Form的程式內容:

<% @ Register TagPrefix="aspcn" Namespace="aspcn" Assembly="aspcn" %>
<html>
<head>
<title></title>
</head>
<body>
<form runat="server">
<aspcn:Header id="MyHeader" runat="server" />
<aspcn:Logon id="MyLogon" runat="server" />
<aspcn:Footer id="MyFooter" runat="server" />
</form>
</body>
</html>

怎麼樣,相當簡單明了吧。
引用我們自訂的控制項,也相當簡潔,只需將Register指令的TagPrefix、Namespace、Assembly屬性全部設定為aspcn。

至此,ASP.Net中三種代碼與頁面構架分離的方法已經介紹完畢。

三種方法各有優劣,本人比較傾向於使用使用者控制項與CodeBinde技術結合使用,因為他們均不需要編譯,相對來說更容易使用,如果您要保護你的代碼,自訂控制項則當然是您最佳的選擇。

希望本文對您的編程有所協助。

pcn:Header id="MyHeader" runat="server" />

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.