日期:2010-05-07  浏览次数:20519 次

  摘要:大多数使用.NET框架组件工作的开发人员的一个核心工作是实现数据访问功能,他们建立的数据访问层(data access layer)是应用程序的精华部分。本文概述了使用Visual Studio .NET和.NET框架组件建立数据访问层需要考虑的五个想法。这些技巧包括通过使用基类(base class)利用面相对象技术和.NET框架组件基础结构,使类容易继承,在决定显示方法和外部界面前仔细地检验需求。

  如果你正在建立以数据为中心(data-centric)的.NET框架组件应用程序,你最终必须建立数据访问层。也许你知道在.NET框架组件中建立自己的代码有很多好处。因为它支持实现和接口(interface)继承,你的代码更容易重复使用,特别是被使用不同的框架组件兼容(Framework-compliant)语言的开发人员使用。本文我将概述为基于.NET框架组件的应用程序建立数据访问层的五条规则。

  开始前,我必须提醒你建立的任何基于本文讨论的规则的数据访问层必须与传统Windows平台上开发人员喜欢的多层或者n层应用程序兼容。在这种结构中,表现层包含Web窗体、Windows窗体、调用与数据访问层的工作相应的事务层的XML服务代码。该层由多个数据访问类(data access classe)组成。换句话说,在事务处理协调不是必要的情况下,表现层将直接调用数据访问层。这种结构是传统的模型-视列表-控制程序(Model-View-Controller,MVC)模式的变体,在多种情况下被Visual Studio .NET和它暴露的控件采用。

  规则1:使用面向对象特性

  最基本的面向对象事务是建立一个使用实现继承的抽象类。这个基类可以包括你的所有数据访问类通过继承能够使用的服务。如果那些服务足够了,它们就能通过在整个组织的基类分布实现重复使用。例如最简单的情况是基类能够为衍生类处理连接的建立过程,如列表1所示。

Imports System.Data.SqlClient

Namespace ACME.Data
Public MustInherit Class DALBase : Implements IDisposable
Private _connection As SqlConnection

Protected Sub New(ByVal connect As String)
_connection = New SqlConnection(connect)
End Sub

Protected ReadOnly Property Connection() As SqlConnection
Get
Return _connection
End Get
End Property

Public Sub Dispose() Implements IDisposable.Dispose
_connection.Dispose()
End Sub

End Class
End Namespace

  列表1.简单基类

  在列表中可以看到,对DALBase类作了MustInherit标记(C#中的抽象),以确保它在继承关系中使用。接着该类在公共构造函数中包括了一个实例化的私有SqlConnection对象,它接收连接字符串作为一个参数。当来自IDisposable接口的Dispose方法确保连接对象已经被配置了的时候,受保护的(protected)Connection属性允许衍生类访问该连接对象。

  即使在下面简化的例子中你也能开始看到抽象基类的用处:

Public Class WebData : Inherits DALBase
Public Sub New()
MyBase.New(ConfigurationSettings.AppSettings("ConnectString"))
End Sub

Public Function GetOrders() As DataSet
Dim da As New SqlDataAdapter("usp_GetOrders", Me.Connection)
da.SelectCommand.CommandType = CommandType.StoredProcedure
Dim ds As New DataSet()

da.Fill(ds)
Return ds
End Function
End Class

  在这种情况下,WebData类继承自DALBase,结果就是不必担心实例化SqlConnection对象,而是通过MyBase关键字(或者C#中的基关键字)简单地把连接字符串传递给基类。WebData类的GetOrders方法能使用Me.Connection(在C#中是this.Connection)访问受保护的属性。虽然这个例子相对简单,但是你将在规则2和3中看到基类也提供了其它的服务。

  当数据访问层必须在COM+环境中运行时抽象的基类很有用。在这种情况下,因为允许组件使用COM+的必要代码复杂得多,所以更好的方式是建立一个如列表2所示的服务组件(serviced component)基类。

Transaction(TransactionOption.Supported), _
EventTrackingEnabled(True)> _
Public MustInherit Class DALServicedBase : Inherits ServicedComponent

Private _connection As SqlConnection

Protected Overrides Sub Construct(ByVal s As String)
_connection = New SqlConnection(s)
End Sub

Protected ReadOnly Property Connection() As SqlConnection
Get
Return _connection
End Get
End Property
End Class

  列表2.服务组件基类

  在这段代码中,DALServicedBase类包含的基本功能与列表1中的相同,但是加上了从System.EnterpriseServices名字空间的ServicedComponent的继承,并且包括了一些属性,指明组件支持对象构造、事务和静态跟踪。接着该基类仔细地捕捉组件服务管理器(Component Services Manager)中的构造字符串并且再次建立和暴露SqlConnection对象。我们要注意的是当一个类继承自DALServicedBase时,它也继承了属性的设置。换句话说,一个衍生类的事务选项也设置为Supported。如果衍生类想重载这种行为,它能在类的层次重新定义该属性。

  此外,衍生类在适当情况下应该有利于自身重载和共享方法。使用重载的方法(一个方法有多个调用信号)在本质上有两种情况。首先,它们在一个方法需要接受多种类型的参数时使用。框架组件中的典型例子是System.Convert类的方法。例如ToString方法包含18个接受一个参数的重载方法,每个重载方法的类型不同。其次,重载的方法用于暴露参数数量不断增长的信号,而不是不同类型的必要参数。在数据访问层中这类重载变得效率很高,因为它能用于为数据检索和修改暴露交替的信号。例如GetOrders方法可以重载,这样一个信号不接受参数并返回所有订单,但是附加的信号接受参数以表明调用程序希望检索特定的顾客订单,代码如下:

Public Overloads Function GetOrders() As DataSet
Public Overloads Function GetOrders(ByVal customerId As Integer) As DataSet

  这种情况下的一个好的实现技巧是抽象GetOrders方法的功能到一个能被每个重载信号调用的私有的或者受保护的方法中。

  共享方法(C#中的静态方法)也能用于暴露数据访问类的所有实例能够访问的字段、属性和方法。尽管共享成员不能与使用组件服务(Component Services)的类一起使用,但是对于在数据访问类的共享构造函数中检索并被所有实例读取的只读数据是有用的。使用共享成员读/写数据时要小心,因为为了访问该共享数据,执行的多个线程可能会竞争。

  规则2:坚持设计指导

  随Visual Studio .NET一起发布的在线文档中有一个叫"类库开发人员的设计指导(Design Guidelines for Class Library Developers)"的主题