[英]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.