ASP.NET终于可以让Web开发人员从ASP的面条代码中脱身出来,以全新的方式来构建Web站点,就像Windows Application一样,我们同样可以用面向对象的、多层的方式来组织和构建Web Application。
下面给出的是一个功能非常简单的留言本程序,旨在揭示ASP.NET强大的能力和全新的开发方式。如果只相对留言本程序本身而言,大家可能怀疑用这么多的气力实现如此简单的程序是否值得,但我说过,例子只是用来说明问题和描述解决方案。其实我认为,从维护和扩充的角度来说,即使再简单的程序,从一开始就进行良好的设计也是非常值得的。
留言本采用多层的方式来构建,下面的介绍为了方便大家理解,并未按照层次的顺序介绍:
一、数据实体(CMessageData类)
CMessageData派生自DataSet,用来维护留言数据,在构造函数中,调用CreateDataTables()来增加一个用来保存留言数据的DataTable,并加到自身的DataTable集合中。静态属性TableMapping用来描述这个DataTable的DataColumn和数据库中物理字段的映射关系,数据访问层将使用这个属性来填充数据进CMessageData对象中。
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属性返回一个新增进数据表的、空的DataRow,用于给逻辑层填充。FillDataFormDataBase()的两个重载调用数据访问层的相应方法来填充一个新的CMessageData对象并返回。UpdateToDatabase用于讲自身的数据更改更新回数据库。
public DataRow AddedNewRow()…
public static CMessageData FillDataFromDatabase(Int32 startRecord, Int32 maxRecord)…
public static CMessageData FillDataFromDatabase(Int32 id)…
public void UpdateToDatabase()…
二、数据访问层(CDataAccess类)
负责连接数据库,进行SIUD(Select,Insert,Update,Delete)操作。数据连接信息放在AppParameters.xml文件中。
FillMessageData()的两个重载创建新的CMessageData对象,填充数据,然后返回:
public static CMessageData FillMessageData(Int32 startRecord, Int32 maxRecord)
public static CMessageData FillMessageData(Int32 id)
UpdateMessageData()把参数中的CMessageData对象所作出的更改更新回数据库:
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");
}
把数据访问层单独提取出来的好处就是其他层都不会直接和数据库打交道,如果我们把数据库从Access改成SqlServer只需要用一个新的CDataAccess类替换现在的即可。在源码中,就有一个使用了Odbc.Net实现的COdbcDataAccess,用这个替换掉CDataAccess不会对程序中其他部分产生任何影响,我们可以利用Odbc.Net的访问能力,把数据库改为Oracle、Forpro等。
三、逻辑层
这个留言本的逻辑层很简单,由三个类组成,CMessage用来描述一条留言,CReply用来描述一条回复,CReplyCollection集合类用来描述多条回复。
CMessage提供了一个重载的构造函数:
public CMessage(DataRow row)
我们可以用CMessageData中个一个DataRow的数据来初始化一个CMessage对象。
public void FillDataRow(DataRow row)
这个函数则把自身的数据填充进参数中的DataRow对象。我们用类似:
GetMessage().FillDataRow(messageData.AddedNewRow())
这样的代码就可以把一条新的留言内容新增到一个CMessageData对象中,其中GetMessage()是页面上收集用户填入的数据并返回一个CMessage的一个方法。
public CReplyCollection Replys
这个属性用来公开对自身这条留言的所有回复。
四、界面层 - 用户控件
为了方便我们把一个CMessage对象和页面上显示出来的一条留言绑定在一起,把一个CReply对象与页面上显示出来的一条回复绑定在一起,我们制作两个UserControl。MessageBlock控件用来显示一条留言,它通过属性:
public CMessage Message
来对象公开CMessage接口,我们只需要把一个CMessage对象赋值给这个属性,就可以让这个控件显示CMessage对象所表示的留言的内容。
ReplyBlock控件用来显示一条回复,同样通过属性:
public CReply Reply
来公开一个CReply类型的接口。
在MessageBlock控件中,我们根据对应的CMessage对象的Replys属性中所包含的回复,通过LoadControl()方法来动态载入ReplyBlock控件,并放置在一个PlaceHolder类型的Web控件中。
五、界面层 - 页面
现在页面的显示非常简单了,我们在主页面(default.aspx.cs)中创建一个CMessageData对象,填充数据,再用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);
}
以一个功能齐全的留言本来衡量,我们上面构建的留言本缺少删贴、管理功能,但是只要基础架构出来,完善和扩展功能是非常简单的。
上面的留言本展示了一个基础的程序架构,真正大型程序的架构可能要复杂上很多。比如数据实体类我们可以把字段信息和映射信息放入一个XML文件中,然后我们只需要创建一个通用的数据实体类,通过载入不同的XML文件就可以描述不同的数据实体;在数据复杂的情况下,维护各个数据间的关系也是一个挑战。
源代码可以在http://www.86soft.com/clsoft/kaneboy/gbook.zip下载到,如果你要用,记住修改一下AppParameters.xml文件,至少里面连接字符串的Access文件的路径是需要更改的(你可以干脆在global.asax中用MapPath()来动态得到这个路径:),CAppParameters.cs文件中的_sAppParameterFileName字符串的值也要根据实际情况作出改变(同样你可以用MapPath():),然后在IIS中建立一个名为gbook的虚拟目录来承载这个项目。
标签: