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#中的base要害字)将连接字符串传递给基类。WebData类的GetOrders方法可以使用Me.Connection(在C#中为this.Connection)访问受保护的属性。尽管该示例相对简单,但假如您看了规则2和规则3的话,就会发现该基类还可以提供其他服务。
Public Overloads Function GetOrders() As DataSet Public Overloads Function GetOrders(ByVal customerId As Integer) As DataSet 在这种情况下,良好的实现技巧是将GetOrders方法的功能抽象到一个可以由每个重载签名调用的私有或受保护的方法中。还可以使用共享方法(在C#中为static方法)来展开可供数据访问类的所有实例访问的字段、属性和方法。尽管不能将共享成员与使用组件服务的类结合使用,但是对于可以在数据访问类的共享构造函数中检索然后被所有实例读取的只读数据来说,它们可能十分有用。在对读/写数据使用共享成员时要非凡小心,这是因为多个执行线程可能会竞争使用对共享数据的访问权。 QQread.com 推出各大专业服务器评测 linux服务器的安全性能 SUN服务器 HP服务器 DELL服务器 IBM服务器 联想服务器 浪潮服务器 曙光服务器 同方服务器 华硕服务器 宝德服务器 规则2:遵守设计准则
在Visual Studio .NET随附的联机文档中,有一个标题为“Design Guidelines for Class Library Developers”的主题,它不仅论述您应该遵循的重载成员、构造函数和事件的模式,而且还讨论了类、属性和方法的命名约定。您应该遵守命名约定的主要原因之一是.NET Framework提供的交叉语言继续。假如您要在Visual Basic .NET中生成一个DAL基类,则您需要确保那些使用与.NET Framework兼容的其他语言的开发人员可以从它继续,并能轻易地理解它的工作方式。按照我概述过的准则去做,那么您的命名约定和结构将不会是特定于语言的了。举个例子,您会在本文的代码示例中注重到,Camel大小写风格(首个单词小写,并夹杂大写字母)用于方法的参数,Pascal大小写风格(每个单词都大写)用于方法,而基类则具有Base后缀以表示它是一个抽象类。.NET Framework设计准则的必然结果是常规设计模式,就像Gang of Four撰写的Design Patterns(Addison-Wesley, 1995)中所介绍的那些设计模式一样。例如,.NET Framework使用了Observer模式的一个名为Event模式的变体(您在类中公开事件时应当遵循该模式)。
Public Sub New(ByVal connect As String) _connection = New SqlConnection(connect) _dalSwitch = New BooleanSwitch("DAL", "Data access Code") End Sub BooleanSwitch的参数包括它的名称和说明。您随后可以添加一个受保护属性,以便将开关打开和关闭,并添加另一个受保护属性,以便使用Trace对象的WriteLineIf方法来格式化和写入跟踪消息:
Protected Property TracingEnabled() As Boolean Get Return _dalSwitch.Enabled End Get Set(ByVal Value As Boolean) _dalSwitch.Enabled = Value End Set End Property
Protected Sub WriteTrace(ByVal message As String) Trace.WriteLineIf(Me.TracingEnabled, Now & ": " & message) End Sub 这样,派生类无须自己了解开关类和侦听器类,就可以在数据访问类中发生重大事件时轻松地调用WriteTrace方法。
其次,为了将表示层从特定的Framework data provider(例如SqlClient或OleDb)分离,调用代码应当使用IDataReader接口而不是具体类型(例如SqlDataReader)来引用返回值。这样,假如应用程序从Oracle移植到SQL Server后端,并且数据访问类中的方法的返回类型进行了更改,则表示层无须更改。假如您希望数据访问类返回XML,则可以从System.Xml命名空间中的XmlDocument和XmlReader类(它们类似于DataSet和IDataReader)中进行选择。换句话说,当数据要从它的源断开连接时,您的方法应当返回XmlDocument(或XmlDataDocument),而XmlReader可以用来对XML数据进行流式访问。
<Serializable()> _ Public Class Book : Implements IComparable
<XmlAttributeAttribute()> Public ProductID As Integer Public ISBN As String Public Title As String Public Author As String Public UnitCost As Decimal Public Description As String Public PubDate As Date
Public Function CompareTo(ByVal o As Object) As Integer _ Implements IComparable.CompareTo
Dim b As Book = CType(o, Book) Return Me.Title.CompareTo(b.Title) End Function
End Class
Public NotInheritable Class BookCollection : Inherits ArrayList
Default Public Shadows Property Item(ByVal productId As Integer) As Book
Get Return Me(IndexOf(productId)) End Get Set(ByVal Value As Book) Me(IndexOf(productId)) = Value End Set End Property
Public Overloads Function Contains(ByVal productId As Integer) As Boolean Return (-1 <> IndexOf(productId)) End Function
Public Overloads Function IndexOf(ByVal productId As Integer) As Integer Dim index As Integer = 0 Dim item As Book
For Each item In Me If item.ProductID = productId Then Return index End If index = index + 1 Next Return -1 End Function
Public Overloads Sub RemoveAt(ByVal productId As Integer) RemoveAt(IndexOf(productId)) End Sub
Public Shadows Function Add(ByVal value As Book) As Integer Return MyBase.Add(value) End Function
最后一个规则指定,为什么应该对在DAL内部使用的.NET Framework data provider抽象化,以及应该如何进行抽象。正如我已经提到的那样,ADO.NET编程模型公开了独特的.NET Framework data provider,包括SqlClient、OleDb和其他可从 MSDN 在线Web站点上获得的data provider。尽管该设计能够提高性能,并且使provider能够公开特定于数据源的功能(例如SqlCommand对象的ExecuteXmlReader方法),但它会迫使开发人员决定针对哪个provider进行编码。换句话说,开发人员通常选择使用SqlClient或OleDb,然后直接针对各个命名空间中的类编写代码。
public enum ProviderType : int {SqlClient = 0, OLEDB = 1}
public class ProviderFactory {
public ProviderFactory(ProviderType provider) { _pType = provider; _initClass(); }
public ProviderType Provider { get { return _pType; } set { if (_pTypeSet) { throw new ReadOnlyException("Provider already set to " + _pType.ToString()); } else { _pType = value; _pTypeSet = true; } } }
public IDataAdapter CreateDataAdapter(string commandText,IDbConnection connection) { IDataAdapter d; IDbDataAdapter da;
d = (IDataAdapter)Activator.CreateInstance(_daType[(int)_pType], false); da = (IDbDataAdapter)d; da.SelectCommand = this.CreateCommand(commandText, connection); return d; }
假如您要更改.NET Framework data provider,则需要重新编写数据访问方法的代码。为了避免这种情况,可以使用称为“抽象工厂(Abstrace Factory)”的设计模式。使用该模式,可以生成一个简单的类,该类将公开能够基于标识传入到构造函数的.NET Framework data provider的信息来创建主要的.NET Framework data provider对象(命令、连接、数据适配器和参数)的方法。图7中的代码显示了该类的一个简化的C#版本。为了使用该类,数据访问类中的代码需要针对.NET Framework data provider实现的各种接口(包括IDbCommand、IDbConnection、IDataAdapter和IDataParameter)进行编程。例如,为了用来自参数化存储过程的结果填充DataSet,您可以在数据访问类的方法内部使用以下代码:
Dim _pf As New ProviderFactory(ProviderType.SqlClient) Dim cn As IDbConnection = _pf.CreateConnection(_connect) Dim da As IDataAdapter = _pf.CreateDataAdapter("usp_GetBook", cn)
Dim db As IDbDataAdapter = CType(da, IDbDataAdapter) db.SelectCommand.CommandType = CommandType.StoredProcedure db.SelectCommand.Parameters.Add(_pf.CreateParameter("@productId", _ DbType.Int32, id))
Dim ds As New DataSet("Books") da.Fill(ds) 通常,您需要在类级别声明ProviderFactory变量,并且在数据访问类的构造函数中将其实例化。另外,就像这里显示的那样,它的构造函数将用从配置文件中读取的data provider填充,而不是硬编码。如您想像的那样,将ProviderFactory添加到DAL基类中会非常美妙,然后可以将它包含在程序集中并分发给其他开发人员。您还可以更深入一步,以封装开发人员反复编写的常见ADO.NET代码。实际上,Microsoft已经发布了一个能够为SQL Server执行该功能的数据访问应用程序块(参见《Data Access Application Block Overview》, http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/daab-rm.asp)。