通用的多层数据访问模式?

我一直在玩一些用于n层数据访问的新模式,并且遇到了一个看起来非常灵活且易于实现的模式。 基本上,我需要一个可以在运行中使各种数据层可插拔/交换的解决方案 – 即从DB进行基本数据访问,分布式缓存,本地缓存等。

下面的代码很容易重复使用并且效率极高 – 只比我以前完全硬编码的解决方案长几个小时。

这看起来怎么样? 有没有什么办法可以更好地实施? 任何一般的想法或批评? 那些使用类似模式的人的任何输入?

基类:

public class DataAdapterFactory where T : class { private DataAdapter Adapter; public DataAdapterFactory(DataAdapterBase Base) { Adapter = Base; } public void Extend() where U : DataAdapterExtender, T, new() { DataAdapterExtender Extender = new U(); Extender.BaseAdapter = Adapter as T; Adapter = Extender; } public T GetAdapter() { return Adapter as T; } } public class DataAdapter where T : class { } public class DataAdapterBase : DataAdapter where T : class { } public class DataAdapterExtender : DataAdapter where T : class { public T BaseAdapter; } 

在DAL中实施:

 // base interface defines methods public interface IMyDataAdapter { string GetString(); } // base sql adapter public class SqlDataAdapter : DataAdapterBase, IMyDataAdapter { public string GetString() { return "SQL"; } } // provides cache support public class DistributedCacheExtender : DataAdapterExtender, IMyDataAdapter { public string GetString() { return BaseAdapter.GetString() + ", Distributed Cache"; } } // provides local cache support public class LocalCacheExtender : DataAdapterExtender, IMyDataAdapter { public string GetString() { return BaseAdapter.GetString() + ", Local Cache"; } } 

访问数据:

 public IMyDataAdapter GetAdapter() { // create adapter based on SqlDataAdapter DataAdapterFactory factory = new DataAdapterFactory(new SqlDataAdapter()); // implement distributed cache factory.Extend(); // implement local cache factory.Extend(); return factory.GetAdapter(); } 

使用上面的工厂,基本适配器和扩展器的任何组合(必须按执行顺序调用Extend ())可以通过接口轻松无缝地使用,业务层对实现一无所知。

在上面的这种情况下,调用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(); } } 

你正在做的事情看起来相当合理,尽管看看这些类如何与它们所在的命名空间和组合相关是有帮助的。

我的经验是抽象出接口背后的数据提供者,数据提供者(有时是接口)生活在一个单独的程序集中(因此:1代表BL,1代表接口,1代表每个数据提供者)。

我定义了围绕业务概念(如IPageDataProvider,ICustomerDataProvider等)的接口而不是数据源(即db表),并且在设计接口时要注意接口Segeration Principle 。

我在运行时通过工厂加载了所需的数据提供程序; 这使用Activator.CreateInstance方法。 工厂从配置中获取它的指令。

因此,当我想要在业务逻辑中使用数据时,我通过工厂(一行代码)创建所需接口实现的实例。

使用这种方法,提供程序不必使用基类,但显然您可以根据需要在数据提供程序中使用基类。

我提出了一种混合抽象工厂,它可以满足你的目的,它也隐藏了开发人员的连接细节。 它提供了一组连接,在我们的例子中,我们每组需要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 '''  ''' The custom connection factory allows higher layers to decide which connection will be returned by the connection proxy '''  '''  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 '''  ''' Reset current connection factory to original connection factory this proxy was instantiated with '''  '''  Public Sub ResetConnection() _CurrentConnectionsFactory = _ConnectionsFactory End Sub '''  ''' Changes the master connection returned for the current connection factory '''  ''' Connection information for master database '''  Public Sub SetMasterConnection(ByVal connectionDetail As DBConnectionDetail) Me.SetAllConnections(connectionDetail, _CurrentConnectionsFactory.GetArchiveConnection, _ _CurrentConnectionsFactory.GetWarehouseConnection, _CurrentConnectionsFactory.GetAuditConnection) End Sub '''  ''' Changes the archive connection returned for the current connection factory '''  ''' Connection information for archive database '''  Public Sub SetArchiveConnection(ByVal connectionDetail As DBConnectionDetail) Me.SetAllConnections(_CurrentConnectionsFactory.GetMasterConnection, connectionDetail, _ _CurrentConnectionsFactory.GetWarehouseConnection, _CurrentConnectionsFactory.GetAuditConnection) End Sub '''  ''' Changes the warehouse connection returned for the current connection factory '''  ''' Connection information for warehouse database '''  Public Sub SetWarehouseConnection(ByVal connectionDetail As DBConnectionDetail) Me.SetAllConnections(_CurrentConnectionsFactory.GetMasterConnection, _CurrentConnectionsFactory.GetArchiveConnection, _ connectionDetail, _CurrentConnectionsFactory.GetAuditConnection) End Sub '''  ''' Changes the audit connection returned for the current connection factory '''  ''' Connection information for audit database '''  Public Sub SetAuditConnection(ByVal connectionDetail As DBConnectionDetail) Me.SetAllConnections(_CurrentConnectionsFactory.GetMasterConnection, _CurrentConnectionsFactory.GetArchiveConnection, _ _CurrentConnectionsFactory.GetWarehouseConnection, connectionDetail) End Sub '''  ''' Sets the current connection factory to a custom connection factory using the supplied connection '''  ''' Connection information for master database ''' Connection information for archive database ''' Connection information for warehouse database '''  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