明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

用多層架構(gòu)構(gòu)建一個(gè)簡易留言本

[摘要]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)目。