ASP.NET的三层结构因为每一层都可以在仅仅更改很少量的代码后,就能放到物理上不同的服务器上使用,因此结构灵活而且性能更佳。此外,每层做些什么其它层是完全看不到的,因此更改、更新某层,都不再需要重新编译或者更改全部的层了。这是个很强大的功能。例如,如果把数据访问代码与业务逻辑层分离,当数据库服务器更改后,你只需要更改数据访问的代码,因为业务逻辑层是不变的,因此不需要更改或者重新编译业务逻辑层。 

  一个N层的应用程序通常有三层:表现层、业务层和数据层。下面让我们看看每层都做些什么。 


  表现层(Presentation Layer) 


  表现层用于用户接口的展示,以及用业务层的类和对象来“驱动”这些接口。 


  在ASP.NET中,该层包括aspx页面、用户控制、服务器控制以及某些与安全相关的类和对象。 


  业务层(Business Tier) 


  业务层用于访问数据层,从数据层取数据、修改数据以及删除数据,并将结果返回给表现层。 


  在ASP.NET 中,该层包括使用SqlClient或OleDb从SQL Server或Access数据库取数据、更新数据及删除数据,并把取得的数据放到DataReader或DataSet中返回给表现层。返回的数据也许只有一个整型数字,比如一个表的行记录数目,但这也要用数据层的数据进行计算。 


  BLL和DAL 


  通常该层被划分成两个子层:业务逻辑层(Business Logic Layer,BLL)和数据访问层(Data Access Layers,DAL)。业务逻辑层在数据访问层之上,也就是说BLL调用DAL的类和对象。DAL访问数据并将其转给BLL。 


  在ASP.NET 中,该层可以用SqlClient或OleDb从SQL Server或Access数据库取数据,把数据通过DataSet 或DataReader的形式给BLL,BLL处理数据给表现层。有的时候,例如直接把DataSet 或DataReader送给表现层的时候,BLL是一个透明层。 


  数据层(Data Tier) 


  数据层是数据库或者数据源。在.NET中,通常它是一个SQL Server或Access数据库,但不仅限于此两种形式,它还可能是Oracle,mySQL,甚至是XML。 


  逻辑层VS(分布式)物理层 


  人们容易将这两个概念搞混。我们说逻辑层是把层按类的集合来划分,而这些层都在同一台个服务器上。(分布式)物理层是指类的集合在不同的服务器上,用附加的代码来处理层间的通信,比如remoting和web服务。 


  决定如何划分你的层(是物理的还是不是物理的)是非常重要的。在划分时应考虑下面因素: 


  1、注意如果划分成物理层,你的应用程序的速度会因为不同服务器在网络中通信的延迟而减慢。所以,如果你决定用物理层,请确保获得性能的提升大于性能的降低。 


  2、按照n层架构设计你的应用程序。 


  3、部署以及维护物理分布式的应用程序的成本是很高的。你首先需要不止一台服务器,你还需要网络硬件来连接这些服务器。在这种情况下,部署应用变得更加复杂!因此这样做之前请确定这样做是否值得。 


  另外还要注意,你的应用程序的每层都做何使用。你也许因为运行的多个服务都需要某一层而把该层放到别台服务器上。例如,你也许会因为给不同的用户定制不同的表现层,而将业务逻辑层放于别处;你也许会因为还有其它的应用访问同一个数据库,而把SQL server服务放到别处。 


  三层C/S的结构特点传统的二层C/S结构有以下几个局限性:


  1、二层C/S结构是单一服务器组成以局域网为中心,难以扩展到大型企业的广域网或Internet。


  2、软硬结合有限。


  3、客户机负荷太重,难以管理大量的客户机。


  4、数据安全性不好。


  正是因为二层C/S有这么多缺点,因此,三层C/S结构应运而生。三层C/S结构是将应用功能分成表示层、功能层和数据层三个部分,如图2所示。


     


  

 


  

图 2 三层C/S结构示意图 


    

表示层

是应用的用户接口部分,它担负着用户与应用间的对话功能。它用于检查用户从键盘等输入的数据,显示应用输出的数据。为使用户能直观地进行操作,一般要使用图形用户接口,操作简单、易学易用。在变更用户接口时,只需改写显示控制和数据检查程序,而不影响其他两层。检查的内容也只限于数据的形式和取值的范围,不包括有关业务本身的处理逻辑。


    

功能层

相当于应用的本体,它是将具体的业务处理逻辑编入程序中。例如,在制作订购合同时要计算合同金额,按照定好的格式配置数据、打印订购合同,而处理所需的数据则要从表示层或数据层取得。表示层和功能层之间的数据交往要尽可能简洁。例如,用户检索数据时,要设法将有关检索要求的信息一次性地传送给功能层,而由功能层处理过的检索结果数据也一次性地传送给表示层。


    通常,在功能层中包含有确认用户对应用和数据库存取权限的功能以及记录系统处理日志的功能。功能层的程序多半是用可视化编程工具开发的,也有使用COBOL和C语言的。


    

数据层

就是数据库管理系统,负责管理对数据库数据的读写。数据库管理系统必须能迅速执行大量数据的更新和检索。因此,一般从功能层传送到数据层的要求大都使用SQL语言。


    

三层C/S的解决方案是:对这三层进行明确分割,并在逻辑上使其独立。原来的数据层作为数据库管理系统已经独立出来,所以,关键是要将表示层和功能层分离成各自独立的程序,并且还要使这两层间的接口简洁明了。


    一般情况是只将表示层配置在客户机中,如果连功能层也放在客户机中,与二层C/S结构相比,其程序的可维护性要好得多,但是其他问题并未得到解决。客户机的负荷太重,其业务处理所需的数据要从服务器传给客户机,所以系统的性能容易变坏。


    如果将功能层和数据层分别放在不同的服务器中,则服务器和服务器之间也要进行数据传送。但是,由于在这种形态中三层是分别放在各自不同的硬件系统上的,所以灵活性很高,能够适应客户机数目的增加和处理负荷的变动。例如,在追加新业务处理时,可以相应增加装载功能层的服务器。因此,系统规模越大这种形态的优点就越显著。


    与传统的二层结构相比,三层C/S结构具有以下优点:   

(1)允许合理地划分三层结构的功能,使之在逻辑上保持相对独立性,从而使整个系统的逻辑结构更为清晰,能提高系统和软件的可维护性和可扩展性。


    (2)允许更灵活有效地选用相应的平台和硬件系统,使之在处理负荷能力上与处理特性上分别适应于结构清晰的三层;并且这些平台和各个组成部分可以具有良好的可升级性和开放性。例如,最初用一台Unix工作站作为服务器,将数据层和功能层都配置在这台服务器上。随着业务的发展,用户数和数据量逐渐增加,这时,就可以将Unix工作站作为功能层的专用服务器,另外追加一台专用于数据层的服务器。若业务进一步扩大,用户数进一步增加,则可以继续增加功能层的服务器数目,用以分割数据库。清晰、合理地分割三层结构并使其独立,可以使系统构成的变更非常简单。因此,被分成三层的应用基本上不需要修正。   (3)三层C/S结构中,应用的各层可以并行开发,各层也可以选择各自最适合的开发语言。使之能并行地而且是高效地进行开发,达到较高的性能价格比;对每一层的处理逻辑的开发和维护也会更容易些。


    (4)允许充分利用功能层有效地隔离开表示层与数据层,未授权的用户难以绕过功能层而利用数据库工具或黑客手段去非法地访问数据层,这就为严格的安全管理奠定了坚实的基础;整个系统的管理层次也更加合理和可控制。


    三、C/S与B/S混合软件体系结构


    B/S与C/S混合软件体系结构是一种典型的异构体系结构。


    B/S软件体系结构,即Browser/Server (浏览器/服务器)结构,是随着Internet技术的兴起,对C/S体系结构的一种变化或者改进的结构。在B/S体系结构下,用户界面完全通过WWW浏览器实现,一部分事务逻辑在前端实现,但是主要事务逻辑在服务器端实现。


    B/S体系结构主要是利用不断成熟的WWW浏览器技术,结合浏览器的多种脚本语言,用通用浏览器就实现了原来需要复杂的专用软件才能实现的强大功能,并节约了开发成本,是一种全新的软件体系结构。基于B/S体系结构的软件,系统安装、修改和维护全在服务器端解决。用户在使用系统时,仅仅需要一个浏览器就可运行全部的模块,真正达到了"零客户端"的功能,很容易在运行时自动升级。B/S体系结构还提供了异种机、异种网、异种应用服务的联机、联网、统一服务的最现实的开放性基础。


    但是,与C/S体系结构相比,B/S体系结构也有许多不足之处,例如:


    (1)B/S体系结构缺乏对动态页面的支持能力,没有集成有效的数据库处理功能。


    (2)B/S体系结构的系统扩展能力差,安全性难以控制。


    (3)采用B/S体系结构的应用系统,在数据查询等响应速度上,要远远地低于C/S体系结构。


    (4)B/S体系结构的数据提交一般以页面为单位,数据的动态交互性不强,不利于在线事务处理(OLTP)应用。


    从上面的对比分析中,我们可以看出,传统的C/S体系结构并非一无是处,而新兴的B/S体系结构也并非十全十美。由于C/S体系结构根深蒂固,技术成熟,原来的很多软件系统都是建立在C/S体系结构基础上的,因此,B/S体系结构要想在软件开发中起主导作用,要走的路还很长。我们认为,C/S体系结构与B/S体系结构还将长期共存。


    我们在实现蓝电变电站信息管理系统解决方案中,就使用了C/S与B/S混合软件体系结构的方式,其结构如图3所示。


     


  

 


  

图3 C/S与B/S混合软件体系结构   变电站内部用户通过局域网直接访问数据库服务器,外部用户(包括县调、地调和省局的用户及普通Internet用户)通过Internet访问Web服务器,再通过Web服务器访问数据库服务器。该解决方案把B/S和C/S这两种软件体系结构进行了有机的结合,扬长避短,有效地发挥了各自的优势。同时,因外部用户只需一台接入Internet的计算机,就可以通过Internet查询运行生产管理情况,无须做太大的投入和复杂的设置。这样也方便所属电业局及时了解各变电站所的运行生产情况,对各变电站的运行生产进行宏观调控。 


    C/S与B/S混合软件体系结构的优点是外部用户不直接访问数据库服务器,能保证企业数据库的相对安全。企业内部用户的交互性较强,数据查询和修改的响应速度较快。


    C/S与B/S混合软件体系结构的缺点是企业外部用户修改和维护数据时,速度较慢,较烦琐,数据的动态交互性不强。


  三层体系结构的对比C/S结构的优势1.三层体系结构我们经常会看到许多应聘者在简历上写着“精通数据库编程”的字样,也经常会在招聘网站上看到软件公司的招聘要求中某一项为“精通数据库编程”。于是这些应聘者去这些软件公司面试,于是我们看到了许多“精通”者落选的现象。


  一些程序员在设计数据库应用时,通常会采用数据控件绑定的方法实现。用鼠标拉几个控件,再用鼠标设置几个属性,连键盘都不用动,就完成了一个数据库应用的开发!当然,这的确是一种快速的数据库应用开发方式,但快速并不意味着精通。


  对于大型的数据库应用系统,或是拥有众多客户端的应用系统,我们需要另外一种“精通”,这就是几乎每个程序员都听说过的“三层体系结构”。


  1.1. 传统的C/S模式在传统的数据库应用体系中,客户端与数据库完全分开,在客户端上运行了大部分服务,如数据访问规则、业务规则、合法性校验等等。每一个客户端都存在数据引擎,并且每个客户端与数据库服务器建立独立的数据库连接(DB Connection)。


  基于该种体系的数据库应用系统的优势:开发周期较短,能够适应大部分中小型数据库应用系统的要求(当客户端数量少于50时)。


  但是,随着数据库应用的日渐发展、数据容量的不断增加、客户端数量的不断增加,该种体系结构显示出了诸多缺陷,主要体现在以下几个方面:


  1、可扩充性:对于数据库服务器端,每当建立一个数据连接,就会占用大量的系统资源,当数据连接达到一定数量(如20个)时,数据库服务器的响应速度与处理速度将大打折扣。


  2、可维护性:基于传统C/S的数据库应用系统,业务规则通常置于客户端应用程序中。如果业务规则一旦发生变化(随便举个例子,如身份证号码有可能升为19位)时,我们就必须修改客户端应用程序,并且将每个客户端进行相应的升级工作。


  3、可重用性:采用传统C/S的设计模式时,数据库访问、业务规则等都固化在客户端应用程序中。如果客户另外提出了B/S的应用需求,则需要在WEB服务器中重新进行数据库访问、业务规则、合法性校验等编码(例如将数据库访问写入ASP代码),而所做的工作与客户端应用程序中的功能完全重复,从而加大了工作量,又使得程序开发者心里感到极不舒服。


  正因为以上的诸多缺陷,使得三层(多层)体系结构成为目前数据库应用开发的首选,甚至客户有时也会提出该种技术需求。


  1.2. 三层体系结构所谓三层体系结构,是在客户端与数据库之间加入了一个“中间层”,也叫组件层。这里所说的三层体系,不是指物理上的三层,不是简单地放置三台机器就是三层体系结构,也不仅仅有B/S应用才是三层体系结构,三层是指逻辑上的三层,即使这三个层放置到一台机器上。


  三层体系的应用程序将业务规则、数据访问、合法性校验等工作放到了中间层进行处理。通常情况下,客户端不直接与数据库进行交互,而是通过COM/DCOM通讯与中间层建立连接,再经由中间层与数据库进行交互。


  这样的好处显而易见:


  1、由于数据访问是通过中间层进行的,因此客户端不再与数据库直接建立数据连接。也就是说,建立在数据库服务器上的连接数量将大大减少。例如一个500个客户端的应用系统,500个客户端分别与中间层服务器建立DCOM连接,而DCOM通讯所占用的系统资源极为有限,并且是动态建立与释放连接,因此客户端数量将不再受到限制。同时,中间层与数据库服务器之间的数据连接通过“连接池”进行连接数量的控制,动态分配与释放数据连接,因此数据连接的数量将远远小于客户端数量。


  2、可维护性得以提高。因为业务规则、合法性校验存在于中间层,因此当业务规则发生改变时,只需更改中间层服务器上的某个组件(如某个DLL文件),而客户端应用程序不需做任何处理,有些时候,甚至不必修改中间层组件,只需要修改数据库中的某个存储过程就可以了。


  3、良好的可重用性。同样,如果需要开发B/S应用,则不必要重新进行数据访问、业务规则等的开发,可以直接在WEB服务器端调用现有的中间层(如可以采用基于IIS的WebClass开发,或直接编写ASP代码)。


  4、事务处理更加灵活,可以在数据库端、组件层、MTS(或COM+)管理器中进行事务处理。


  前一段时间帮一个项目组做他们的项目,有幸了解了一下他搭建的架构。相比起以前所见过的架构,我觉得这个应该算是不错的。大体结构如下图:


  

在编码中体会到这个架构有以下几个优点:


  1、 层与层之间依赖于接口:


  UI依赖于IBLL,IBLL依赖于IDAL,这样做在设计模式中叫做依赖倒置。也就是说依赖于抽象,而不是具体实现。如果今后的业务逻辑有变动可以不变程序的主体框架,灵活性较好。


  2、 使用Castle对类进行管理:


  由于层与层之间使用接口连接,但是毕竟要实现多态已实现实际的业务逻辑。使用Castle对类的管理机制,以依赖注入的方式,将接口对应的子类关联起来。这样做可以把变化放到运行时,维护性较好。


  3、 Singleton模式的应用


  关键的类型因为长期使用,而且其本身占用资源较高,使用Singleton模式,用长期占用空间换取因实例化而造成的时间上的浪费。性能较好。


  

觉得还存在的问题:


  对于复杂业务逻辑处理的还是不太理想:


  对于一个事物里有多个操作的问题是这样解决的,在IDAL中有一个方法的入口点,IBLL调用这个入口方法。在DAL扩展这个方法时进行对多个方法业务上的拼装。这样感觉好像把业务层的任务放到了数据层解决。


  不过对于这个问题,现在找到了一些解决方法,在VS2003中可以使用TransactionAttribute属性将操作放到一个事务中。在VS2005中有一个Transaction的类(听说的),可以很好的解决这个问题。


  下面研究一下这个问题,当然重心放在VS2005上,毕竟VS2003将是过去时,何况TransactionAttribute是COM+中的东西,使用起来觉得不方便。


  在这次项目开发中,我们对以前用的三层结构有进行了进一步的改变,除了使用Castle的Windsor容器来管理BLL层和DAL层,在数据的封装和对数据的读取上比以前更加面向对象。


  1、 对于BLL层和DAL层的类型,分别继承各自的IBLL和IDAL,使用Windsor容器以注入的方式对其进行实例化,这一点和上次一样,不再赘述。


  2、 对于Model层有了一些改进。每一个Model类型会对应一个ModelCollection集合类型,例如:对于订单Order实体会有相对应的OrderCollection实体集合类型。这个集合类型继承自List<T>类型,如下:


  public class OrderCollection:List<Order>


  //List<T>是一个泛型类型


  3、 对于ModelCollection类型中只有一个方法,就是将读取出的数据集合转化为指定Model的对象集合


  4、 在Model定义中也和以往简单的数据表字段封装不同,应该说加入了少量的业务逻辑。比如说:


  Order的实体类型中定义了OrderDetailCollection,也就是说明Order与OrderDetail是一对多的关系,OrderDetail实体中加入一个Order实体对象


  5、 对于日志的实现加入了AOP的思想,使用Castle中的Facility.AspectSharp,对于要加入日志的方法加了一个Attribute标签,在容器中截获方法调用的消息。这样可以减少在系统成型后加入日志带来的修改量


  项目中的心得:


  1、 对于三层来说UI层、BLL层和DAL层要分别写入相应的代码,在这次项目中感觉我们往往会把业务层中的代码写到其他的层中,比如说:有一个需求是这样的,用户要减少订单中某件物品的采购数量,当用户减少部分数量时,应该对相应记录使用Update操作,当用户减少全部数量时,应该删除相应的记录。我原来对这种判断往往会放在UI层来判断,这次项目中我体会到,对于UI层应该只是调用一种方法,BLL层做判断


  UI:Click()


  {


  BLL.Update();


  }


  BLL:


  Update()


  {


  If(UpdateFlag)


  {


  DAL.Update();


  }


  Else


  {


  DAL.Delete();


  }


  }


  DAL:


  Update();


  Delete();


  还有,比如用户在界面上的多个类似改变状态的按钮,对于UI层来说应该是多个方法,调用的BLL也应该是多个方法,但是DAL层才应该调用一个方法,而不应该将要改变的状态在UI层就组装好。


  UI:


  ChangeStateToTrue()


  {


  BLL.ChangeStateToTrue();


  }


  ChangeStateToFalse()


  {


  BLL.ChangeStateToFalse();


  }


  BLL:


  ChangeStateToTrue()


  {


  ChangeState (True)


  }


  ChangeStateToFalse()


  {


  ChangeState (False)


  }


  DAL:


  ChangeState(State)


  这些问题也许并不感觉什么严重,但是层次间的功能相对更清晰,也有助于我们进行横向开发


  2、 在系统中往往会存在很多的状态,将这些状态提取出来,作为一个单独的项目,可以使用枚举来封装,可以提高代码的可读性,也给今后做这件事的人以方便,而且可以到达统一的效果.


  3、 系统中的综合查询部分,对于结果的传递没有使用实体类进行传递,后来感觉综合查询本身就很灵活多变,用实体类去传递结果,显得有些困难。而且结果集本身就是依靠多个实体及实体间关系得出,又如何用一个实体去传递。即使对于每一个查询建立一个实体,对于今后的改动又如何很好的应对呢


  项目中的一些遗憾:


  开始想使用Castle中的AR建立ORM,但是出了一些问题,应该算是有些遗憾吧。所以后来自己去写实体中的关系,有些地方还是有些牵强,今后要加强学习


  

Model类型:加入两个构造函数 付值和缺省去掉ModelList类型,使用List<Model>代替,可以在相应的Model中加入得到List的方法对于有关系的列表,还是使用DataSet比较方便DAL对于读取数据,不必加入Try{}Catch{}对于操作数据库时出现的问题可以使用自定义的异常处理方式使用Partial将带有业务逻辑的方法和普通的添查删改方法分开BLL加入缺省构造函数和对应DAL实例使用Partial将带有业务逻辑的方法和普通的添查删改方法分开UI按模块划分,设置文件夹最外层只留下公共页面,如Login, ErrorPage添加和编辑可以使用同一页面,对于添加时的主键可以设置为0,在BLL层中判断具体调用DAL中的什么方法对于传递的参数,最好使用对称加密方式,提高安全性

  自定义异常处理在这点时间的项目中,发现有些异常被抛出后,不能正常抛到报错页面进行处理,实际上可以在Catch的时候进行处理,记录错误在这次项目中发现如果是post back时发生异常,应用程序是不能重新转向报错页面的,所以使用自定义的异常处理权限控制。