三层架构设计课案.docx
- 文档编号:14438028
- 上传时间:2023-06-23
- 格式:DOCX
- 页数:37
- 大小:365.39KB
三层架构设计课案.docx
《三层架构设计课案.docx》由会员分享,可在线阅读,更多相关《三层架构设计课案.docx(37页珍藏版)》请在冰点文库上搜索。
三层架构设计课案
第八章三层架构设计
在软件体系架构设计中,分层式结构是最常见,也是重要的一种结构。
微软推荐的分层式结构一般分为三层,从下至上分别为:
数据访问层、业务逻辑层、表示层。
8.1三层架构概述
与网络协议是分层一样,软件设计也要进行分层,分层的目的是为了实现“高内聚、低耦合”,采用“分而治之”的思想,把任务划分成子任务,逐个解决,易于控制,易于延展,易于多个进行项目合作。
所谓的三层架构就是将整个业务应用划分为表示层、业务逻辑层和数据访问层,由数据访问层去访问数据库,十分有利于系统的开发、维护、部署和扩展。
那么我们为什么要使用分层开发呢,它有什么独特的优势呢?
对于简单的应用来说,没有必要搞得那么复杂,可以不进行分层,但是对一个大型系统来说这样的设计的缺陷就很严重了。
面向对象的程序设计模式追求的是代码的通用性,可移植性,可维护性、功能扩展,分层开发这种设计模式体现了面向对象的思想,而在页面的后台代码中直接访问数据库,实际上是打着面向对象的幌子却依然走着面向过程的老路。
试问一下,我们用Access做后台开发的未分层程序,如果有一天因为数据量的增加,安全的需要等,数据库有Access变成了SQLServer,怎么办?
网页代码文件中的所有程序都要重新修改,整个系统需要重新来做,这都是设计不合理惹的祸。
多层开发架构的出现很有效的解决了这样的问题。
三层架构中,各个层之间的分工是很明确的。
面向对象嘛,就像一个公司中的部门一样,每个部门的分工是不一样的,是哪个部门的任务就有哪个部门完成,对应的,各个部门的维护工作也是各自完成且不会影响其它的部门,至少影响不是很大,否则就只能说明分工还不合理。
采用三层架构设计系统,各层高内聚、低耦合,通过有效的协作来完成系统的高效运行,三层架构中出现上面说的问题,由于其将数据访问操作完全限定在数据访问层内,数据库发生了改变,我们只需要修改数据访问层,其它的地方不用修改。
三层架构中各层的功能是这样的。
1、表示层(UI):
通俗讲就是展现给用户的界面,是用户在使用系统时的所见所得,表示层负责直接跟用户进行交互,用于数据录入,数据显示等。
表示嘛,也就意味着侧重于做与布局和外观显示方面的工作,以及客户端的验证和处理等,并针对用户的请求去调用业务逻辑层的功能。
2、业务逻辑层(BLL):
针对表示层提交的请求,进行逻辑处理,如果需要访问数据库,就调用数据访问层的操作,对数据库进行操作。
3、数据访问层(DAL):
顾名思义,就是用于专门跟后台数据库进行交互,直接操纵数据库,实现数据库记录的增加、删除、修改、查询等。
与具体数据库系统相关的对象只在这一层被引用,如System.Data。
System.Data.SqlClient等命名空间的对象,数据访问层之外的地方都不应该出现对这些对象的引用。
三层架构的框架模型如图8-1所示。
图8-1三层架构框架模型
理想的分层式架构,应该是一个支持可抽取、可替换的“抽屉”式架构。
这个框架模型中,出现了一个自定义实体类,现在大家都倾向于用自定义实体数据形式在层与层之间以及层内模块间进行数据传输。
实体类是现实世界中实体对象在计算机中的表示。
一般来说,实体类可以分为“贫血实体类”和“充血实体类”,前者仅仅保存实体的属性,而后者还包含一些实体间的关系与逻辑。
我们这里所用的实体类都是“贫血实体类”。
大多情况下,实体类和数据库中的表是对应的,实体类的属性和表的字段对应,但这并不是一个限制,有可以出现一个实体类对应多个表,或者交叉对应的情况。
虽然现在分层的设计开发中,一般都是用实体类对应数据库的表。
但是有些专家意见是慎用,因为如果把数据展示在页面上的话,从数据库中读出的DataSet本身就是XML形式,数据展示也用XML,如果用了实体类就多了一次转化。
图7.2显示了实体对象在三层架构中传递数据的过程。
图8-2实体类在三层架构中的数据传递
分层的思想讲完了,在多人合作开发系统的过程中,就可以按层来划分任务,只要设计的时候把接口定义好,开发人员就可以同时开发,而且不会发生冲突,做前台的人不需要关心怎么实现到数据库中去查询、更新、删除和增加数据,他们只需要去调用相应的类就可以了。
做数据访问层的人也不需要知道前台的事,定义好与其它层交互的接口,规定好参数就行,各个层都一样,做好自己的工作就可以了。
这样的系统,清晰性、可维护性和可扩展性都非常强大,测试和修改也比较方便。
下面结合具体的实例,来学习三层架构的应用。
仍以“BookShopOnNet”数据库中顾客表“ShopUser”为例,用三层架构的方式实现对它的显示,以及增、删、改、查等操作。
顾客表的结构为:
ShopUser(UserIdintIDENTITY(1,1)NOTNULL,UserNamevarchar(30)NOTNULL,Passwordsvarchar(20)NOTNULL,Emailvarchar(30),XinMinnvarchar(5),Sexbit,Birthdaydatetime,Addressnvarchar(50),Telvarchar(12),Photovarchar(50),Nationnvarchar(15),Hobbyvarchar(50),PersonURLvarchar(50)。
详细说明请见第五章。
8.2三层架构应用实例
下面来构建这个应用程序解决方案。
启动VS2010,依次单击菜单“文件”|“新建”|“项目”,弹出“新建项目”对话框,在“已安装的模板”下,单击展开“其他项目类型”折叠菜单,再单击“VisualStudio解决方案”,最后单击右边的“空白解决方案”,选择好解决方案存放的路径,输入解决方案名“ThreeLayerApp”,确定后就创建了一个空白解决方案。
以后在这个空白解决方案中,添加各层对应的项目或站点,一个多项目解决方案的应用程序就构成了。
8.2.1实体类设计
实体类的设计比较简单,它一般与数据库中的表一一对应,针对每个表建一个实体类,表的字段对应实体类的属性,下面以实例具体介绍。
在VS中右击解决方案资源管理器窗格中的“ThreeLayerApp”解决方案名,在弹出的快捷菜单中,依次单击“添加”|“新建项目”,弹出“添加新项目”对话框,在“已安装的模板”下,选中“VisualC#”,选择右边的“类库”,输入项目名“BookShopModel”并确定,这样就在此解决方案下添加了一个实体类项目,然后在此项目中添加实体类即可。
接下来在这个实体类项目中逐个添加实体。
右击项目名“BookShopModel”,选“添加”|“类”,输入类名“ShopUserModel”,它同时也做文件名,这个实体类文件就建好了,在其中输入类的私有成员变量,并依据这些成员变量构建相应的属性。
大家应该清楚私有成员变量和相应属性的关系吧,属性实质是访问器,有读写特性及判断处理功能,它本身不保存数据,真正的数据是保存在它所读写的私有成员变量中,属性提高了私有成员变量的安全性。
当类的私有成员变量输好后,属性可以用“重构”|“封装字段”的方式快速构建,并根据属性的读写特性及是否需要判断处理功能,进行适当的修改,这些基本操作不再详述。
为了使类能在项目外其他项目中访问,把类加“public”属性,这样构建出来的顾客实体类ShopUserModel的部分内容如下:
publicclassShopUserModel
{
privateint_UserId;
privatestring_UserName;
……
publicintUserId
{
get{return_UserId;}
set{_UserId=value;}
}
publicstringUserName
{
get{return_UserName;}
set{_UserName=value;}
}
……
}
8.2.2数据访问层设计
数据访问层是专门跟后台数据库进行交互,直接操纵数据库,实现数据库记录的增加、删除、修改、查询等。
首先在解决方案中添加名为“BookShopDALLayer”的数据访问层项目,操作方法仍是右击解决方案名,依次单击“添加”|“新建项目”,弹出“添加新项目”对话框,选中“VisualC#”右边的“类库”,输入项目名并确定。
此项目产生后,由于其中的类要用到前面刚创建的项目名为“BookShopModel”的实体类,所以需要添加对它的引用,否则其中的实体类是不能被这个项目中的类所访问的。
添加引用的方法是:
右击此项目中的“引用”,选“添加引用”,弹出如图8-3所示的对话框,选择“项目”选择卡,其中列出了解决方案中的所有项目,选中实体类项目后确定即可。
图8-3添加引用对话框
然后用在此项目中添加“类”的方式,添加“ShopUserDAL”数据访问类,此类中要进行数据库的访问操作,所以在类的开头要导入下面的几个命名空间,以便使用其中的类。
usingSystem.Configuration;
usingSystem.Data;
usingSystem.Data.SqlClient;
usingBookShopModel;
用“usingBookShopModel;”导入这个命名空间,是因为前面创建实体类时,用的是默认的命名空间,默认命名空间就是项目名。
这样构建出来的顾客表所对就的数据访问类ShopUserDAL的内容如下:
publicclassShopUserDAL
{
staticstringstrconn=ConfigurationManager.ConnectionStrings["connStr"].ConnectionString;
SqlConnectionconn;
publicShopUserDAL()
{
conn=newSqlConnection(strconn);
}
///
///获取顾客信息数据表视图
///
///
publicDataViewGetDataView()
{
SqlCommandcomm=newSqlCommand("SELECTUserId,UserName,Passwords,Email,XinMin,Sex,Birthday,Address,Tel,Photo,Nation,Hobby,PersonURLFROMShopUser",conn);
using(SqlDataAdaptersda=newSqlDataAdapter())
{
sda.SelectCommand=comm;
DataSetds=newDataSet();
sda.Fill(ds,"ShopUser");
returnds.Tables["ShopUser"].DefaultView;
}
}
publicintDeleteByUserId(intUserId)
{
stringsqltext=string.Format("DeleteFROMShopUserWHEREUserId={0}",UserId);
using(SqlCommandcomm=newSqlCommand(sqltext,conn))
{
conn.Open();
intresult=comm.ExecuteNonQuery();
conn.Close();
returnresult;
}
}
publicintUpdatePartInfoByUserId(ShopUserModelobjUser)
{
stringsqltext=string.Format("UPDATEShopUserSETXinMin='{1}',Sex='{2}',Birthday='{3}',Email='{4}'WHEREUserId={0}",objUser.UserId,objUser.XinMin,objUser.Sex,objUser.Birthday,objUser.Email);
using(SqlCommandcomm=newSqlCommand(sqltext,conn))
{
conn.Open();
intresult=comm.ExecuteNonQuery();
conn.Close();
returnresult;
}
}
publicintUpdateByUserId(ShopUserModelobjUser)
{
stringsqltext=string.Format("UPDATEShopUserSETUserName='{1}',Email='{2}',XinMin='{3}',Sex='{4}',Birthday='{5}',Address='{6}',Tel='{7}',Photo='{8}',Nation='{9}',Hobby='{10}',PersonURL='{11}'WHEREUserId={0}",objUser.UserId,objUser.UserName,objUser.Email,objUser.XinMin,objUser.Sex,objUser.Birthday,objUser.Address,objUser.Tel,objUser.Photo,objUser.Nation,objUser.Hobby,objUser.PersonURL);
using(SqlCommandcomm=newSqlCommand(sqltext,conn))
{
conn.Open();
intresult=comm.ExecuteNonQuery();
conn.Close();
returnresult;
}
}
publicShopUserModelGetModelByUserId(intUserId)
{
ShopUserModelobjUser=newShopUserModel();
stringstrSql="SELECTUserId,UserName,Passwords,Email,XinMin,Sex,Birthday,Address,Tel,Photo,Nation,Hobby,PersonURLFROMShopUserWHEREUserId="+UserId.ToString();
using(SqlCommandcomm=newSqlCommand(strSql,conn))
{
conn.Open();
using(SqlDataReadersdr=comm.ExecuteReader())
{
if(sdr.Read())
{
objUser.UserId=Convert.ToInt32(sdr["UserId"].ToString());
objUser.UserName=sdr["UserName"].ToString();
objUser.Passwords=sdr["Passwords"].ToString();
objUser.Email=sdr["Email"].ToString();
objUser.XinMin=sdr["XinMin"].ToString();
if(!
Convert.IsDBNull(sdr["Sex"]))
{
objUser.Sex=Convert.ToBoolean(sdr["Sex"]);
}
if(!
Convert.IsDBNull(sdr["Birthday"]))
{
objUser.Birthday=Convert.ToDateTime(sdr["Birthday"]);
}
objUser.Address=sdr["Address"].ToString();
objUser.Tel=sdr["Tel"].ToString();
objUser.Photo=sdr["Photo"].ToString();
objUser.Nation=sdr["Nation"].ToString();
objUser.Hobby=sdr["Hobby"].ToString();
objUser.PersonURL=sdr["PersonURL"].ToString();
}
sdr.Close();
}
conn.Close();
}
returnobjUser;
}
}
8.2.3业务逻辑层设计
业务逻辑层针对表示层提交的请求,进行逻辑处理,如果需要访问数据库,就调用数据访问层的操作,对数据库进行操作。
在解决方案中添加名为“BookShopBLLLayer”的业务逻辑层项目,操作方法与添加数据访问层类似,仍是用添加“类库”的方式添加一个项目,输入项目名并确定即定。
项目产生后,要用到前面创建的“BookShopModel”实体类项目和“BookShopDALLayer”数据访问类项目,所以也需要添加对它们的引用,添加引用的方法与以前是一样的。
这样构建出来的顾客表所对就的数据访问类ShopUserBLL的内容如下:
publicclassShopUserBLL
{
ShopUserDALoShopUserDAL=newShopUserDAL();
///
///获?
取¨?
顾?
客¨ª信?
息¡é数ºy据Y表À¨ª视º¨®图ª?
///
///
publicDataViewGetDataView()
{
returnoShopUserDAL.GetDataView();
}
///
///按ã¡ä用®?
户¡ì编À¨¤号?
删¦?
除y用®?
户¡ì
///
///
///
publicintDeleteByUserId(intUserId)
{
returnoShopUserDAL.DeleteByUserId(UserId);
}
///
///按ã¡ä用®?
户¡ì编À¨¤号?
更¨¹新?
用®?
户¡ì部?
分¤?
信?
息¡é
///
///
///
publicintUpdatePartInfoByUserId(ShopUserModelobjUser)
{
returnoShopUserDAL.UpdatePartInfoByUserId(objUser);
}
///
///按ã¡ä用®?
户¡ì编À¨¤号?
更¨¹新?
用®?
户¡ì信?
息¡é
///
///
///
publicintUpdateByUserId(ShopUserModelobjUser)
{
returnoShopUserDAL.UpdateByUserId(objUser);
}
///
///按ã¡ä用®?
户¡ì编À¨¤号?
获?
取¨?
用®?
户¡ì实º¦Ì体¬?
信?
息¡é
///
///
///
publicShopUserModelGetModelByUserId(intUserId)
{
returnoShopUserDAL.GetModelByUserId(UserId);
}
}
8.2.4表示层设计
表示层就是展现给用户的界面,表示层负责直接跟用户进行交互,用于数据录入,数据显示等。
在解决方案中添加名为“WebSite”的站点项目,操作方法是,右击解决方案名,选“添加”|“新建网站”,选择“ASP.NET空网站”,选择站点存放路径后确定即可。
添加网面,添加控件,布局和美化界面,并编写相关的事件,在事件中调用底层提供的功能。
GridView控件
GridView是ASP.NET中功能非常丰富的控件之一,它以表格的形式显示数据库的内容。
GridView内置提供了选择、排序、分页、编辑、更新和删除功能。
1GridView的列
在一般情况下,当为GridView设定数据源后,就可以直接显示数据了,原因是它的lAutoGenerateColumns属性为True,GridView会使用反射来处理所有字段并按发现的次序自动生成列,自动生成列的标题是表中的字段名,使用默认的控件及格式。
这种自动生成列的功能对快速创建页面非常有效,但缺乏灵活性。
如果希望隐藏某些列,或改变显示顺序,或以自定义格式显示,就必须设置AutoGenerateColumns属性为False来关闭自动生成列,手动控制列字段的设计。
GridView提供了如下7种类型的列字段,各种列字段及其功能如下:
●BoundField:
以文本形式显示数据的普通绑定列,以DataField属性设定数据从数据源哪个字段取数,用“DataFormatString”属性来设置显示格式。
●CheckBoxField:
以复选框形式显示数据,绑定到此种列的数据应该是布尔型的,用“DataField”属性设置数据从数据源的哪个字段中取数。
●HyperLinkField:
用超链接形式的显示数据,利用此列的“DataNavigateUrlFields”和“DataNavigateUrlFormatString”两个属性,配合构建超链接的URL,用“DataTextField”属性来设置超链接显示的文本。
●ImageField:
以Image图象形式显示数据,列的ImageUrl属性指出图片源。
●ButtonField:
自定义按钮列,默认以“Button”按钮形式显示,通过设置它的“ButtonType”属性,可以以链接按钮或图象按钮形式显示,其功能灵活,比如购物系统中的“购买”按钮,图书借阅系统中的“借出”等都是这种自定义按钮。
当然“购买”按钮显示为图象按钮可能显得美观一些。
在GridView中添加的所有的ButtonField自定义按钮列,其事件响应都必须写在GridView的RowCommand事件中,以CommandName来区别是哪个按钮的响应。
●CommandField:
系统内置的一些数据操作按钮,有“编辑”、“更新”、“取消”、“选择”和“删除”五个按钮,它们都
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 三层 架构 设计