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)。