[英]Generic multi-layer data access pattern?
我一直在研究一些用於n層數據訪問的新模式,並且遇到了一種看起來非常靈活且易於實現的模式。 基本上,我需要一個可以使各種數據層即時可插拔的解決方案-即,從數據庫訪問基本數據,分布式緩存,本地緩存等。
下面的代碼易於重用且效率極高-僅比我以前的完全硬編碼的解決方案長幾格。
看起來如何? 有什么辦法可以更好地實現呢? 有什么一般的想法或批評嗎? 那些使用過類似模式的人有什么意見嗎?
基類:
public class DataAdapterFactory<T> where T : class
{
private DataAdapter<T> Adapter;
public DataAdapterFactory(DataAdapterBase<T> Base)
{
Adapter = Base;
}
public void Extend<U>() where U : DataAdapterExtender<T>, T, new()
{
DataAdapterExtender<T> Extender = new U();
Extender.BaseAdapter = Adapter as T;
Adapter = Extender;
}
public T GetAdapter()
{
return Adapter as T;
}
}
public class DataAdapter<T> where T : class { }
public class DataAdapterBase<T> : DataAdapter<T> where T : class { }
public class DataAdapterExtender<T> : DataAdapter<T> where T : class
{
public T BaseAdapter;
}
在DAL中實施:
// base interface defines methods
public interface IMyDataAdapter
{
string GetString();
}
// base sql adapter
public class SqlDataAdapter : DataAdapterBase<IMyDataAdapter>, IMyDataAdapter
{
public string GetString()
{
return "SQL";
}
}
// provides cache support
public class DistributedCacheExtender : DataAdapterExtender<IMyDataAdapter>, IMyDataAdapter
{
public string GetString()
{
return BaseAdapter.GetString() + ", Distributed Cache";
}
}
// provides local cache support
public class LocalCacheExtender : DataAdapterExtender<IMyDataAdapter>, IMyDataAdapter
{
public string GetString()
{
return BaseAdapter.GetString() + ", Local Cache";
}
}
訪問數據:
public IMyDataAdapter GetAdapter()
{
// create adapter based on SqlDataAdapter
DataAdapterFactory<IMyDataAdapter> factory = new DataAdapterFactory<IMyDataAdapter>(new SqlDataAdapter());
// implement distributed cache
factory.Extend<DistributedCacheExtender>();
// implement local cache
factory.Extend<LocalCacheExtender>();
return factory.GetAdapter();
}
使用上面的工廠,基本適配器和擴展器的任何組合(必須按執行順序調用Extendd <>())都可以通過接口輕松,無縫地即時使用,而業務層對實現一無所知。
在上述情況下,調用GetString()將導致“ SQL,分布式緩存,本地緩存”。 在現實世界中,本地緩存將首先被調用。 如果某個項目不存在,我們將轉到分布式緩存,如果不存在,我們將從數據庫中獲取它-根據需要,可以根據需要交換任何模塊,用戶等。
為此,我將看一下http://en.wikipedia.org/wiki/Decorator_pattern-您的示例將變成這樣:
public interface IMyDataAdapter
{
string GetString();
}
public class SqlDataAdapter : IMyDataAdapter
{
public string GetString()
{
return "SQL";
}
}
public class LocalCacheDecorator : IMyDataAdapter
{
private IMyDataAdapter adapter;
public LocalCacheDecorator(IMyDataAdapter adapter)
{
this.adapter = adapter;
}
public string GetString()
{
return "Local cache, " + this.adapter.GetString();
}
}
您正在做的事情看起來相當合理,盡管從類中所涉及的名稱空間和可配置性的角度來看這些類之間的關系會有所幫助。
我的經驗是圍繞接口背后的數據提供程序進行抽象,而數據提供程序(有時是接口)位於單獨的程序集中(因此:BL代表1個,接口代表1個,每個數據提供者1個)。
我圍繞業務概念(例如IPageDataProvider,ICustomerDataProvider等)定義接口,而不是數據源(即db表)定義接口,並且您在設計接口時要謹記接口隔離原則 。
我在運行時通過工廠加載了所需的數據提供程序; 這使用Activator.CreateInstance方法。 工廠從config獲取指令。
因此,當我想在業務邏輯中使用數據時,我通過工廠(一行代碼)創建了所需接口實現的實例。
通過這種方法,提供程序不必使用任何基類,但是顯然,如果需要,您可以在數據提供程序內使用基類。
我想出了一種混合抽象工廠,可以滿足您的目的,它也隱藏了開發人員的連接詳細信息。 它提供了一組連接,在我們的情況下,每組需要4個連接。 這四個將由“連接集”工廠返回,還有一個“自定義連接集”工廠,您可以在其中隨時更改返回的連接。 您使用代理訪問連接對象。 然后,我通過單例訪問代理,這樣就可以在應用程序加載事件或global.asmx中對其進行設置,然后就可以輕松交換正在使用的連接。 雖然您可以在運行時進行交換。 希望這可以幫助。
它是為我的情況專門編寫的,因此對您來說可能有點過大?
請注意,這是針對npgsql的,您可以輕松地將其更改為標准.data。 客戶端基類或sqlclent類...
Public MustInherit Class DBConnectionDetail
'note this abstract class could be an interface if you didn't want these common methods
Protected _conStrBldr As New Npgsql.NpgsqlConnectionStringBuilder
Protected _connection As Npgsql.NpgsqlConnection
Public Sub New()
'Set the connection builder properties in the subclass
End Sub
Friend ReadOnly Property ConnectionStringBuilder() As Npgsql.NpgsqlConnectionStringBuilder
Get
Return _conStrBldr
End Get
End Property
Friend Property Connection() As Npgsql.NpgsqlConnection
Get
Return _connection
End Get
Set(ByVal value As Npgsql.NpgsqlConnection)
_connection = value
End Set
End Property
' Misc properties - information for programmers of higher layers
Public MustOverride ReadOnly Property Description() As String
Public MustOverride ReadOnly Property HostName() As String
Public MustOverride ReadOnly Property IP() As String
Public MustOverride ReadOnly Property Database() As String
End Class
Public Class LocalArchiveConnectionDetails
Inherits DBConnectionDetail
Public Sub New()
_conStrBldr.Host = "localhost"
_conStrBldr.Port = 5432
_conStrBldr.UserName = "usr"
_conStrBldr.Password = "pwd"
_conStrBldr.Database = "archive"
'_conStrBldr.Pooling = True
'_conStrBldr.MinPoolSize = 5
'_conStrBldr.MaxPoolSize = 10
'_conStrBldr.CommandTimeout = 1024
'_conStrBldr.Timeout = 1024
'_conStrBldr.ConnectionLifeTime = 2
End Sub
Public Overrides ReadOnly Property Description() As String
Get
Return "Local Connection to Database"
End Get
End Property
Public Overrides ReadOnly Property Database() As String
Get
Return "archive"
End Get
End Property
Public Overrides ReadOnly Property HostName() As String
Get
Return "local host"
End Get
End Property
Public Overrides ReadOnly Property IP() As String
Get
Return "127.0.0.1"
End Get
End Property
End Class
Public Interface IConnectionFactory
ReadOnly Property GetMasterConnection() As DBConnectionDetail
ReadOnly Property GetWarehouseConnection() As DBConnectionDetail
ReadOnly Property GetArchiveConnection() As DBConnectionDetail
ReadOnly Property GetAuditConnection() As DBConnectionDetail
End Interface
Public Class DBConnectionBuilder
Friend Shared Function GetConnection(ByVal conStrBldr As DBConnectionDetail) As NpgsqlConnection
Return New NpgsqlConnection(conStrBldr.ConnectionStringBuilder.ConnectionString)
End Function
'Friend Shared Function GetConnection(ByVal conStr As String) As NpgsqlConnection
' Return New NpgsqlConnection(conStr)
'End Function
End Class
Public Class LocalConnectionFactory
Implements IConnectionFactory
Public ReadOnly Property GetArchiveConnection() As DBConnectionDetail Implements IConnectionFactory.GetArchiveConnection
Get
Dim dbConnection As New LocalArchiveConnectionDetails
dbConnection.Connection = DBConnectionBuilder.GetConnection(dbConnection)
Return dbConnection
End Get
End Property
Public ReadOnly Property GetMasterConnection() As DBConnectionDetail Implements IConnectionFactory.GetMasterConnection
Get
Dim dbConnection As New LocalMasterConnectionDetails
dbConnection.Connection = DBConnectionBuilder.GetConnection(dbConnection)
Return dbConnection
End Get
End Property
Public ReadOnly Property GetWarehouseConnection() As DBConnectionDetail Implements IConnectionFactory.GetWarehouseConnection
Get
Dim dbConnection As New LocalWarehouseConnectionDetails
dbConnection.Connection = DBConnectionBuilder.GetConnection(dbConnection)
Return dbConnection
End Get
End Property
Public ReadOnly Property GetAuditConnection() As DBConnectionDetail Implements IConnectionFactory.GetAuditConnection
Get
Dim dbConnection As New LocalAuditConnectionDetails
dbConnection.Connection = DBConnectionBuilder.GetConnection(dbConnection)
Return dbConnection
End Get
End Property
End Class
''' <summary>
''' The custom connection factory allows higher layers to decide which connection will be returned by the connection proxy
''' </summary>
''' <remarks></remarks>
Public Class CustomConnectionFactory
Implements IConnectionFactory
Private _archiveConnection As DBConnectionDetail
Private _masterConnection As DBConnectionDetail
Private _warehouseConnection As DBConnectionDetail
Private _auditConnection As DBConnectionDetail
Friend Sub New()
End Sub
Friend Sub New(ByVal masterConnection As DBConnectionDetail, ByVal archiveConnection As DBConnectionDetail, _
ByVal warehouseConnection As DBConnectionDetail, ByVal auditConnection As DBConnectionDetail)
_masterConnection = masterConnection
_archiveConnection = archiveConnection
_warehouseConnection = archiveConnection
_auditConnection = auditConnection
End Sub
Friend Sub SetMasterConnectionDetail(ByVal connectionDetail As DBConnectionDetail)
_masterConnection = connectionDetail
End Sub
Friend Sub SetArchiveConnectionDetail(ByVal connectionDetail As DBConnectionDetail)
_archiveConnection = connectionDetail
End Sub
Friend Sub SetWarehouseConnectionDetail(ByVal connectionDetail As DBConnectionDetail)
_warehouseConnection = connectionDetail
End Sub
Friend Sub SetAuditConnectionDetail(ByVal connectionDetail As DBConnectionDetail)
_auditConnection = connectionDetail
End Sub
Public ReadOnly Property GetArchiveConnection() As DBConnectionDetail Implements IConnectionFactory.GetArchiveConnection
Get
_archiveConnection.Connection = DBConnectionBuilder.GetConnection(_archiveConnection)
Return _archiveConnection
End Get
End Property
Public ReadOnly Property GetMasterConnection() As DBConnectionDetail Implements IConnectionFactory.GetMasterConnection
Get
_masterConnection.Connection = DBConnectionBuilder.GetConnection(_masterConnection)
Return _masterConnection
End Get
End Property
Public ReadOnly Property GetWarehouseConnection() As DBConnectionDetail Implements IConnectionFactory.GetWarehouseConnection
Get
_warehouseConnection.Connection = DBConnectionBuilder.GetConnection(_warehouseConnection)
Return _warehouseConnection
End Get
End Property
Public ReadOnly Property GetAuditConnection() As DBConnectionDetail Implements IConnectionFactory.GetAuditConnection
Get
_auditConnection.Connection = DBConnectionBuilder.GetConnection(_auditConnection)
Return _auditConnection
End Get
End Property
End Class
Public Class DBConnectionsProxy
Private _ConnectionsFactory As IConnectionFactory
Private _CurrentConnectionsFactory As IConnectionFactory
Public Sub New(ByVal connectionFactory As IConnectionFactory)
'check that a connection factory is provided otherwise nothing will work
If connectionFactory Is Nothing Then
Throw New NullReferenceException("You must provide a connection factory")
Else
_ConnectionsFactory = connectionFactory
_CurrentConnectionsFactory = connectionFactory
End If
End Sub
Friend Property ConnectionFactory() As IConnectionFactory
Get
Return _CurrentConnectionsFactory
End Get
Set(ByVal value As IConnectionFactory)
_CurrentConnectionsFactory = value
End Set
End Property
Public ReadOnly Property GetMasterConnection() As Npgsql.NpgsqlConnection
Get
Return _CurrentConnectionsFactory.GetMasterConnection.Connection
End Get
End Property
Public ReadOnly Property GetArchiveConnection() As Npgsql.NpgsqlConnection
Get
Return _CurrentConnectionsFactory.GetArchiveConnection.Connection
End Get
End Property
Public ReadOnly Property GetWarehouseConnection() As Npgsql.NpgsqlConnection
Get
Return _CurrentConnectionsFactory.GetWarehouseConnection.Connection
End Get
End Property
Public ReadOnly Property GetAuditConnection() As Npgsql.NpgsqlConnection
Get
Return _CurrentConnectionsFactory.GetAuditConnection.Connection
End Get
End Property
''' <summary>
''' Reset current connection factory to original connection factory this proxy was instantiated with
''' </summary>
''' <remarks></remarks>
Public Sub ResetConnection()
_CurrentConnectionsFactory = _ConnectionsFactory
End Sub
''' <summary>
''' Changes the master connection returned for the current connection factory
''' </summary>
''' <param name="connectionDetail">Connection information for master database</param>
''' <remarks></remarks>
Public Sub SetMasterConnection(ByVal connectionDetail As DBConnectionDetail)
Me.SetAllConnections(connectionDetail, _CurrentConnectionsFactory.GetArchiveConnection, _
_CurrentConnectionsFactory.GetWarehouseConnection, _CurrentConnectionsFactory.GetAuditConnection)
End Sub
''' <summary>
''' Changes the archive connection returned for the current connection factory
''' </summary>
''' <param name="connectionDetail">Connection information for archive database</param>
''' <remarks></remarks>
Public Sub SetArchiveConnection(ByVal connectionDetail As DBConnectionDetail)
Me.SetAllConnections(_CurrentConnectionsFactory.GetMasterConnection, connectionDetail, _
_CurrentConnectionsFactory.GetWarehouseConnection, _CurrentConnectionsFactory.GetAuditConnection)
End Sub
''' <summary>
''' Changes the warehouse connection returned for the current connection factory
''' </summary>
''' <param name="connectionDetail">Connection information for warehouse database</param>
''' <remarks></remarks>
Public Sub SetWarehouseConnection(ByVal connectionDetail As DBConnectionDetail)
Me.SetAllConnections(_CurrentConnectionsFactory.GetMasterConnection, _CurrentConnectionsFactory.GetArchiveConnection, _
connectionDetail, _CurrentConnectionsFactory.GetAuditConnection)
End Sub
''' <summary>
''' Changes the audit connection returned for the current connection factory
''' </summary>
''' <param name="connectionDetail">Connection information for audit database</param>
''' <remarks></remarks>
Public Sub SetAuditConnection(ByVal connectionDetail As DBConnectionDetail)
Me.SetAllConnections(_CurrentConnectionsFactory.GetMasterConnection, _CurrentConnectionsFactory.GetArchiveConnection, _
_CurrentConnectionsFactory.GetWarehouseConnection, connectionDetail)
End Sub
''' <summary>
''' Sets the current connection factory to a custom connection factory using the supplied connection
''' </summary>
''' <param name="masterConnectionDetail">Connection information for master database</param>
''' <param name="archiveConnectionDetail">Connection information for archive database</param>
''' <param name="warehouseConnectionDetail">Connection information for warehouse database</param>
''' <remarks></remarks>
Public Sub SetAllConnections(ByVal masterConnectionDetail As DBConnectionDetail, _
ByVal archiveConnectionDetail As DBConnectionDetail, _
ByVal warehouseConnectionDetail As DBConnectionDetail, _
ByVal auditConnectionDetail As DBConnectionDetail)
Dim customConnFactory As New CustomConnectionFactory
customConnFactory.SetMasterConnectionDetail(masterConnectionDetail)
customConnFactory.SetArchiveConnectionDetail(archiveConnectionDetail)
customConnFactory.SetWarehouseConnectionDetail(warehouseConnectionDetail)
customConnFactory.SetAuditConnectionDetail(auditConnectionDetail)
_CurrentConnectionsFactory = customConnFactory
End Sub
End Class
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.