您的位置:首页 > 编程语言 > VB

VB.NET分层开发的几点心得[转载]

2006-11-30 01:30 204 查看
  随着微软.NET开发平台的推出,无疑给无数的VB开发者带来了福音,无继承,丑陋的错误捕捉,无对象池,不能进行底层开发,这些都成为过去,随之而来的是完全的面向OO特性,丰富的类库,支持XML,当有人更钟情于C#,C++开发者更是对此不屑一顾,但软件开发中更重要的是一种思想和原理,语言只是一种工具。

  我有幸参与了一个基于三层架构的ASP.NET的项目,也有了些心得,下面就开发过程中遇到的一些问题谈谈自己的一点看法。以下是架构图:



 

  一、数据实体层(Entity)的实现

  我们首先需要解决的是数据的表示方式的问题,在VB开发中,相信大家都遇到过如何有效的构造数据实体的问题,单个类,集合类,集合类中如何操作单个类,如何用填充数据实体,这些问题解决起来都不是很容易,也有很多方法来实现,对于集合类的实现,可以用数组,可以用集合对象,也可以用字典对象,当然实例化数据实体也有很多方法,象最常见的工厂模式,这里就不讨论了,我在项目中是用TYPED-DATASET对象来作为数据实体,个人觉得有以下好处:

  1. 数据绑定。可以直接和控件绑定,尤其是网格控件,在VB中是很困难的,除非增加个属性来存放Recordset,这样一来又要多增加方法来初始化Recordset,并且增加了实例化和封送处理的成本

  2. 代码自动生成。少写很多代码,尤其是当表中字段很多的时候,并且自动提供了序列化功能

  3. 集合类。Typed-Dataset本身就是个集合类,提供添加,删除,修改,查找单个类的方法

  当然也有缺点,任何事物都无法十全十美,比如很难从中派生出子类,实例化成本高等。

  在自动生成Typed-Dataset时,需要做些改动使Entity更好的符合OO的特性

  A、自动生成的表对象和行对象是以DataTable和Row结尾的,这可以通过加如以下代码来更改:在XML文件中的<xml:schema 节点加入:

xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:codegen="urn:schemas-microsoft-com:xml-msprop" 命名空间

  B、重写 <xs:choice maxOccurs="unbounded"> 节点后的<xs:element name="Tables”> 节点 <xs:element name="Tables" codegen:typedName="Table" codegen:typedPlural="Tables">

  C、由于自动生成的代码遇到某个字段为NULL时默认是报错,如想不报错,需在每个Element节点定义中加入: nillable="true" codegen:nullValue="_null" 或nillable="true" codegen:nullValue="false" 或nillable="true" codegen:nullValue="1900-01-01T00:00:00"

  D、Entity的粒度。在考虑实体对象的设计时,“对象的粒度”是一个需要仔细考虑的问题,个人喜欢粗粒度的Entity,这样可以减少与数据库的往返次数,当然也不是包含所有的表,这样效率不高,比如Order,只需要Order 和OrderDetail 表,Product,SalesPerson就不需要了,当然也可以用从dataset继承的自定义类来作为Entity,但个人觉得一般情况下没必要,毕竟MS的typed-dataset功能更多,而且实现起来也比较复杂。

 

  二、 数据服务层的实现(Data Service)

  使用MS的Data Access Application Block,效果还可以,大家可以到MS站点下载。

  三、数据访问层(Data Access)

  所有的数据操作采用传统的存储过程(SP)实现的,但由于Entity是个集合,包含多行,因此要循环调用SP以实现批量操作,个人建议提供两个方法,一个是操作一行(常用),一个是操作多行(不常用)。

  四、业务层(Business Logics)

  需要完成的功能是各种业务规则和逻辑的实现,从SevicedComponent继承,这是界面层唯一需要清楚了解的层,也是以后系统扩展和经常需要变更的地方,因此业务层的每个模块都有个基类,Module1BaseClass,例如单据模块,结构如下:

Abstract Class OrderBase

Public Function AddNew()

Public Function Delete

Public Function GetOrderByID

Public Function Update

End class

 

Public Class SaleOrder Inherits OrderBase

……………

End Class

 

Public Class PurchaseOrder Inherits OrderBase

………

End Class

 

Public Class MorePurchaseOrder Inherits PurchaseOrder

………

End Class

  这样一来业务层的扩展性就强了,这就是OO的好处啊,如果在VB中,就很难实现了,当然通过Interface也可以间接的实现,但不方便,毕竟VB6不是面向OO的语言,是面向Interface的。

  五、Facade层

  提供更高层次的业务逻辑处理,一般是当表现层的某个操作需要关连到多个模块时由这层提供一个统一的接口,但并不要求表现层必须调用这层来处理业务逻辑。

 

  六、事务的处理(Transaction)

  采用手工处理事务的方式,由于所有的业务层都是Serviced component,因此我们可以直接利用COM+中的事务功能,虽然Serviced component有点性能损耗,但在分布式的结构中还是会带来很多方便的,我将所有的业务层的事务属性设为Required,具体代码如下:

<Description ("Contains methods for inserting and updating product records in the berp system."), Guid ("4FE02C4F-C8FA-497c-9A9E-EDA0877B772C"), Transaction (TransactionOption. Supported), Synchronization (SynchronizationOption.Required), JustInTimeActivation(True)>

Public Class Product

Public Function Add() As Boolean

'…………

If ContextUtil.IsInTransaction Then

ContextUtil.SetComplete()

Else

ContextUtil.SetAbort()

End If

'…………

End Function

End Class

  系统中的其它层不需要作为Serviced component

 

  七、异常的处理

  彻底改变了VB6中的错误处理方式,这点我最喜欢,哈! 项目中的异常分为两种,业务异常和系统异常:

  1、业务Exception:

  从ApplicationException中继承,有一个总的Exception,然后各个模块的Exception都是从总的Exception中继承,从而形成了一个层次结构:

ApplicationException

ProjectException

Module1Exception

Module1Concret1Exception

Module2Concret2Exception

……………

Module2Exception

……………

  2、系统异常,报出的错误,包括数据库报出的:

  异常捕捉的原则是只捕捉需要的错误,因材在数据访问层和业务层不需要Catch所有的错误,如下:

Public Function Methoda()

IF …… THEN

If ContextUtil。IsInTransaction Then

ContextUtil.etAbort()

End If

Throw Module1Concret1Exception

End If

……

Catch 需要的具体的错误Module2Concret1Exception

'这里不需要 Catch ProjectException

End Function

  为了便于调试,需要把系统异常记录在日志文件中,这里用的MS提供的Exception Management Application Block 来实现的,具体的实现方式见MSDN,下载地址是:

http:www.icrosoft.om/downloads/details.spx?FamilyId=8CA8EB6E-6F4A-43DF-ADEB-8F22CA173E02&displaylang=en

  在界面显示错误信息的时候为两种显示方式,一种是用来显示业务异常的,一种界面是用来显示系统异常的,代码如下:

Public Sub Button_OnClick()

Dim objblModule As 业务层

Try

ObjblModul.osomething()

Catch ProjectExcption

'显示一个定制的页面()

Catch Exception

'call Exception Management. Publish method to log the exception

'显示另一个定制的页面,定制的页面有将具体的错误信息发送到administrator的功能,就象windows的错误页面

End Try

End Sub

  八、权限控制

  这是一个值得深入讨论的问题,我采用的方式是用FORM认证的方法,具体的用户信息,权限是放在数据库中,并没有集成WINDOWS的域认证,实现的时候用专门的层来执行权限判断,利用GeneralPrincipal 和GeneralIdentity对象 ,代码片段如下:

 

Public Function CheckRole(ByVal strRole As String) As Boolean

Return privateUserPrincipal.IsInRole(strRole)

End Function

 

Private Sub InitPrincipal()

Try

privateUserIdentity = New GenericIdentity(privateUserName)

privateUserPrincipal = New GenericPrincipal(privateUserIdentity, privateUserRoles)

Catch e As Exception

Throw New Exception("an error occurred setting credentials")

End Try

End Sub

 

Private Sub SavePrincipal()

Try

If Not IsNothing(_context) Then

context.Session("UserName") = privateUserIdentity.Name

context.Session("Roles") = privateUserRoles

context.User = privateUserPrincipal

End If

Catch e As Exception

Throw e

End Try

End Sub

  这样一来当界面变成WINDOWS的FORM是就不需要改动很多代码了,同时为了解决将权限放到SESSION中引起的延时问题,我将用户的权限信息放在服务端的XML文件中,然后直接中XML文件中获得数据,任何对用户信息的修改都将改变相应的XML文件,这样效率高于从数据库获得。

  当然在实际开发中还会碰到很多其他问题,如报表,打印,并发性等,如果大家有兴趣可以和我联系,限于个人水平有不足之处请多多包含。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: