2010-06-03 67 views
2

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

下面的代码很容易重复使用,效率非常高比我以前完全硬编码的解决方案延长了几个钟头。

这是怎么回事?有什么办法可以更好地实施?任何一般的想法或批评?来自使用类似模式的人的任何输入?

基类:

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(); 
} 

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

在上面的例子中,调用GetString()会导致“SQL,分布式缓存,本地缓存”。在现实世界中,本地缓存将首先被调用。如果某个项目不在那里,我们将转到分布式缓存,如果它不在那里,我们会从DB获取 - 根据需要,可以根据需要交换任何模块,用户等

回答

1

我想看看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(); 
    } 
} 
+0

绝对是一个选项。最初导致我离开的原因是,当你实现一个新的“装饰器”时,需要编写构造器,但我可能只是在挑剔...... – Andrew 2010-06-03 22:02:53

+0

这种方法更偏向于继承的组合,并且在使用依赖注入的情况下工作得很好。 – 2010-06-04 05:09:14

0

你在做什么看起来相当明智的,尽管这将会是有益的看看这些类是如何与它们所处的命名空间和集合相关的。

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

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

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

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

通过这种方法,没有提供商可供使用的基类,但显然如果您愿意,可以在数据提供者中使用基类。

0

我想出了一种混合抽象工厂,它可以满足你的目的,它也隐藏了开发人员的连接细节。它提供了一组连接,在我们的例子中,我们需要每组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