用多層架構(gòu)構(gòu)建一個(gè)簡易留言本
發(fā)表時(shí)間:2023-08-18 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]kaneboy@163.net ASP.NET終于可以讓W(xué)eb開發(fā)人員從ASP的面條代碼中脫身出來,以全新的方式來構(gòu)建Web站點(diǎn),就像Windows Application一樣,我們...
kaneboy@163.net
ASP.NET終于可以讓W(xué)eb開發(fā)人員從ASP的面條代碼中脫身出來,以全新的方式來構(gòu)建Web站點(diǎn),就像Windows Application一樣,我們同樣可以用面向?qū)ο蟮摹⒍鄬拥姆绞絹斫M織和構(gòu)建Web Application。
下面給出的是一個(gè)功能非常簡單的留言本程序,旨在揭示ASP.NET強(qiáng)大的能力和全新的開發(fā)方式。如果只相對(duì)留言本程序本身而言,大家可能懷疑用這么多的氣力實(shí)現(xiàn)如此簡單的程序是否值得,但我說過,例子只是用來說明問題和描述解決方案。其實(shí)我認(rèn)為,從維護(hù)和擴(kuò)充的角度來說,即使再簡單的程序,從一開始就進(jìn)行良好的設(shè)計(jì)也是非常值得的。
留言本采用多層的方式來構(gòu)建,下面的介紹為了方便大家理解,并未按照層次的順序介紹:
一、數(shù)據(jù)實(shí)體(CMessageData類)
CMessageData派生自DataSet,用來維護(hù)留言數(shù)據(jù),在構(gòu)造函數(shù)中,調(diào)用CreateDataTables()來增加一個(gè)用來保存留言數(shù)據(jù)的DataTable,并加到自身的DataTable集合中。靜態(tài)屬性TableMapping用來描述這個(gè)DataTable的DataColumn和數(shù)據(jù)庫中物理字段的映射關(guān)系,數(shù)據(jù)訪問層將使用這個(gè)屬性來填充數(shù)據(jù)進(jìn)CMessageData對(duì)象中。
public class CMessageData : DataSet {
public CMessageData() {
this.CreateDataTables();
}
public static DataTableMapping TableMapping {
get {
DataTableMapping result = new DataTableMapping("t_gbook_postinfo", "MessageTable");
result.ColumnMappings.Add("id", "Id");
result.ColumnMappings.Add("last_reply_time", "LastReplyTime");
//…..
return result;
}
}
private void CreateDataTables() {
DataTable dt = new DataTable("MessageTable");
dt.Columns.Add("Id", typeof(Int32));
dt.Columns.Add("LastReplyTime", typeof(DateTime));
// …..
dt.Columns["Id"].AutoIncrement = true;
dt.Columns["Id"].AutoIncrementSeed = 0;
dt.Columns["Id"].AutoIncrementStep = -1;
dt.PrimaryKey = new DataColumn[] {dt.Columns["Id"]};
this.Tables.Add(dt);
}
AddedNewRow屬性返回一個(gè)新增進(jìn)數(shù)據(jù)表的、空的DataRow,用于給邏輯層填充。FillDataFormDataBase()的兩個(gè)重載調(diào)用數(shù)據(jù)訪問層的相應(yīng)方法來填充一個(gè)新的CMessageData對(duì)象并返回。UpdateToDatabase用于講自身的數(shù)據(jù)更改更新回?cái)?shù)據(jù)庫。
public DataRow AddedNewRow()…
public static CMessageData FillDataFromDatabase(Int32 startRecord, Int32 maxRecord)…
public static CMessageData FillDataFromDatabase(Int32 id)…
public void UpdateToDatabase()…
二、數(shù)據(jù)訪問層(CDataAccess類)
負(fù)責(zé)連接數(shù)據(jù)庫,進(jìn)行SIUD(Select,Insert,Update,Delete)操作。數(shù)據(jù)連接信息放在AppParameters.xml文件中。
FillMessageData()的兩個(gè)重載創(chuàng)建新的CMessageData對(duì)象,填充數(shù)據(jù),然后返回:
public static CMessageData FillMessageData(Int32 startRecord, Int32 maxRecord)
public static CMessageData FillMessageData(Int32 id)
UpdateMessageData()把參數(shù)中的CMessageData對(duì)象所作出的更改更新回?cái)?shù)據(jù)庫:
public static Int32 UpdateMessageData(CMessageData messageData) {
OleDbConnection conn = new OleDbConnection(CAppParameters.OleDbConnectionString);
OleDbCommand cmdSelect = new OleDbCommand("Select username,last_reply_time,guest_name,guest_email,guest_website_name,guest_website_url,guest_oicq,guest_ip,guest_post_time,guest_text,reply_data From t_gbook_postinfo", conn);
OleDbCommand cmdInsert = new OleDbCommand();
cmdInsert.Connection = conn;
cmdInsert.CommandText = "Insert Into t_gbook_postinfo (last_reply_time,guest_name,guest_email,guest_website_name,guest_website_url,guest_oicq,guest_ip,guest_post_time,guest_text,reply_data) Values (@last_reply_time,@guest_name,@guest_email,@guest_website_name,@guest_website_url,@guest_oicq,@guest_ip,@guest_post_time,@guest_text,@reply_data)";
cmdInsert.Parameters.Add("@last_reply_time", OleDbType.DBDate, 0, "last_reply_time");
cmdInsert.Parameters.Add("@guest_name", OleDbType.VarWChar, 255, "guest_name");
//…
OleDbCommand cmdUpdate = new OleDbCommand();
cmdUpdate.Connection = conn;
cmdUpdate.CommandText = "Update t_gbook_postinfo Set last_reply_time=@last_reply_time,guest_name=@guest_name,guest_email=@guest_email,guest_website_name=@guest_website_name,guest_website_url=@guest_website_url,guest_oicq=@guest_oicq,guest_ip=@guest_ip,guest_post_time=@guest_post_time,guest_text=@guest_text,reply_data=@reply_data Where (id=@Original_id)";
cmdUpdate.Parameters.Add("@last_reply_time", OleDbType.DBDate, 0, "last_reply_time");
cmdUpdate.Parameters.Add("@guest_name", OleDbType.VarWChar, 255, "guest_name");
//…
OleDbCommand cmdDelete = new OleDbCommand();
cmdDelete.Connection = conn;
cmdDelete.CommandText = @"Delete From t_gbook_postinfo where (id = @Original_id)";
cmdDelete.Parameters.Add("@Original_id", OleDbType.Integer, 0, "id").SourceVersion = DataRowVersion.Original;
OleDbDataAdapter ada = new OleDbDataAdapter(cmdSelect);
ada.InsertCommand = cmdInsert;
ada.UpdateCommand = cmdUpdate;
ada.DeleteCommand = cmdDelete;
ada.TableMappings.Add(CMessageData.TableMapping);
return ada.Update(messageData, "t_gbook_postinfo");
}
把數(shù)據(jù)訪問層單獨(dú)提取出來的好處就是其他層都不會(huì)直接和數(shù)據(jù)庫打交道,如果我們把數(shù)據(jù)庫從Access改成SqlServer只需要用一個(gè)新的CDataAccess類替換現(xiàn)在的即可。在源碼中,就有一個(gè)使用了Odbc.Net實(shí)現(xiàn)的COdbcDataAccess,用這個(gè)替換掉CDataAccess不會(huì)對(duì)程序中其他部分產(chǎn)生任何影響,我們可以利用Odbc.Net的訪問能力,把數(shù)據(jù)庫改為Oracle、Forpro等。
三、邏輯層
這個(gè)留言本的邏輯層很簡單,由三個(gè)類組成,CMessage用來描述一條留言,CReply用來描述一條回復(fù),CReplyCollection集合類用來描述多條回復(fù)。
CMessage提供了一個(gè)重載的構(gòu)造函數(shù):
public CMessage(DataRow row)
我們可以用CMessageData中個(gè)一個(gè)DataRow的數(shù)據(jù)來初始化一個(gè)CMessage對(duì)象。
public void FillDataRow(DataRow row)
這個(gè)函數(shù)則把自身的數(shù)據(jù)填充進(jìn)參數(shù)中的DataRow對(duì)象。我們用類似:
GetMessage().FillDataRow(messageData.AddedNewRow())
這樣的代碼就可以把一條新的留言內(nèi)容新增到一個(gè)CMessageData對(duì)象中,其中GetMessage()是頁面上收集用戶填入的數(shù)據(jù)并返回一個(gè)CMessage的一個(gè)方法。
public CReplyCollection Replys
這個(gè)屬性用來公開對(duì)自身這條留言的所有回復(fù)。
四、界面層 - 用戶控件
為了方便我們把一個(gè)CMessage對(duì)象和頁面上顯示出來的一條留言綁定在一起,把一個(gè)CReply對(duì)象與頁面上顯示出來的一條回復(fù)綁定在一起,我們制作兩個(gè)UserControl。MessageBlock控件用來顯示一條留言,它通過屬性:
public CMessage Message
來對(duì)象公開CMessage接口,我們只需要把一個(gè)CMessage對(duì)象賦值給這個(gè)屬性,就可以讓這個(gè)控件顯示CMessage對(duì)象所表示的留言的內(nèi)容。
ReplyBlock控件用來顯示一條回復(fù),同樣通過屬性:
public CReply Reply
來公開一個(gè)CReply類型的接口。
在MessageBlock控件中,我們根據(jù)對(duì)應(yīng)的CMessage對(duì)象的Replys屬性中所包含的回復(fù),通過LoadControl()方法來動(dòng)態(tài)載入ReplyBlock控件,并放置在一個(gè)PlaceHolder類型的Web控件中。
五、界面層 - 頁面
現(xiàn)在頁面的顯示非常簡單了,我們?cè)谥黜撁?default.aspx.cs)中創(chuàng)建一個(gè)CMessageData對(duì)象,填充數(shù)據(jù),再用LoadControl()方法來載入MessageBlock控件來顯示留言就可以了。
CMessageData messageData = CMessageData.FillDataFromDatabase((iPage - 1) * iPageSize, iPageSize);
for(Int32 i = 0; i < messageData.Count; ++i) {
MessageBlock msg = (MessageBlock)LoadControl("MessageBlock.ascx");
msg.Message = new CMessage(messageData.Tables["MessageTable"].Rows[i]);
hldMessage.Controls.Add(msg);
}
以一個(gè)功能齊全的留言本來衡量,我們上面構(gòu)建的留言本缺少刪貼、管理功能,但是只要基礎(chǔ)架構(gòu)出來,完善和擴(kuò)展功能是非常簡單的。
上面的留言本展示了一個(gè)基礎(chǔ)的程序架構(gòu),真正大型程序的架構(gòu)可能要復(fù)雜上很多。比如數(shù)據(jù)實(shí)體類我們可以把字段信息和映射信息放入一個(gè)XML文件中,然后我們只需要?jiǎng)?chuàng)建一個(gè)通用的數(shù)據(jù)實(shí)體類,通過載入不同的XML文件就可以描述不同的數(shù)據(jù)實(shí)體;在數(shù)據(jù)復(fù)雜的情況下,維護(hù)各個(gè)數(shù)據(jù)間的關(guān)系也是一個(gè)挑戰(zhàn)。
源代碼可以在http://www.86soft.com/clsoft/kaneboy/gbook.zip下載到,如果你要用,記住修改一下AppParameters.xml文件,至少里面連接字符串的Access文件的路徑是需要更改的(你可以干脆在global.asax中用MapPath()來動(dòng)態(tài)得到這個(gè)路徑:),CAppParameters.cs文件中的_sAppParameterFileName字符串的值也要根據(jù)實(shí)際情況作出改變(同樣你可以用MapPath():),然后在IIS中建立一個(gè)名為gbook的虛擬目錄來承載這個(gè)項(xiàng)目。