C# 通用多层数据访问模式?

C# 通用多层数据访问模式?,c#,design-patterns,generics,architecture,C#,Design Patterns,Generics,Architecture,我一直在研究一些新的n层数据访问模式,发现了一种似乎非常灵活且易于实现的模式。基本上,我需要一个解决方案,可以使各种数据层在运行中可插拔/可交换,即从数据库访问基础数据、分布式缓存、本地缓存等 下面的代码很容易重用,而且效率极高——只比我以前完全硬编码的解决方案长几格 这个看起来怎么样?有什么办法可以更好地实施这一点吗?有什么一般性的想法或评论吗?使用过类似模式的人有什么意见吗 基类: public class DataAdapterFactory<T> where T : clas

我一直在研究一些新的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;
}
// 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();
}
公共类DataAdapterFactory,其中T:class
{
专用数据适配器;
公共DataAdapterFactory(DataAdapterBase)
{
适配器=底座;
}
public void Extend(),其中U:DataAdapterXtender,T,new()
{
DataAdapterXtender Extender=新U();
Extender.BaseAdapter=适配器为T;
适配器=扩展器;
}
公共GetAdapter()
{
将适配器返回为T;
}
}
公共类DataAdapter,其中T:class{}
公共类DataAdapterBase:DataAdapter,其中T:class{}
公共类DataAdapterXtender:DataAdapter其中T:class
{
公共T-BaseAdapter;
}
在DAL中实施:

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;
}
// 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();
}
//基本接口定义方法
公共接口IMyDataAdapter
{
字符串GetString();
}
//基本sql适配器
公共类SqlDataAdapter:DataAdapterBase,IMyDataAdapter
{
公共字符串GetString()
{
返回“SQL”;
}      
}
//提供缓存支持
公共类分布式CacheExtender:DataAdapterXtender、IMyDataAdapter
{
公共字符串GetString()
{
返回BaseAdapter.GetString()+“,分布式缓存”;
}   
}
//提供本地缓存支持
公共类LocalCacheExtender:DataAdapterXtender、IMyDataAdapter
{
公共字符串GetString()
{
返回BaseAdapter.GetString()+“,本地缓存”;
}
}
访问数据:

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;
}
// 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();
}
公共IMyDataAdapter GetAdapter() { //基于SqlDataAdapter创建适配器 DataAdapterFactory=新的DataAdapterFactory(新的SqlDataAdapter()); //实现分布式缓存 factory.Extend(); //实现本地缓存 factory.Extend(); 返回factory.GetAdapter(); } 使用上面的工厂,基本适配器和扩展器的任何组合(必须按照执行顺序调用Extend())都可以通过接口轻松无缝地动态使用,而业务层对实现一无所知

在上面的这种情况下,调用GetString()将导致“SQL、分布式缓存、本地缓存”。在真实场景中,将首先调用本地缓存。如果一个项目不在那里,我们将前往分布式缓存,如果它不在那里,我们将从数据库中获取它-任何模块都可以根据需要根据obejct、用户等进行交换。

我将查看用于此目的的缓存-然后您的示例将变成这样:

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、ICCustomerDataProvider等)而不是数据源(如db表)定义接口的,在设计接口时,您需要注意这些接口

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

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


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

我提出了一种混合抽象工厂,可以满足您的需要,它还可以对开发人员隐藏连接细节。它提供了一组连接,在我们的例子中,每个连接需要4个。这四个将由“连接集”工厂返回,还有一个“自定义连接集工厂”,您可以随时更改返回的连接。您可以使用代理访问连接对象。 然后,我通过一个单例访问代理,这样我就可以在我的应用程序加载事件或global.asmx中设置它,然后就可以很容易地交换正在使用的连接。尽管您可以在运行时进行交换。希望这有帮助

这是专门为我的场景编写的,所以对你来说可能有点过分了吧

请注意,这是针对npgsql的,您可以轻松地将其更改为standard.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
Public必须继承类DBConnectionDetail
'注意,如果您不需要这些常用方法,这个抽象类可以是一个接口
受保护的_conStrBldr作为新的Npgsql.NpgsqlConnectionStringBuilder
作为Npgsql.NpgsqlConnection的受保护的_连接
公共分新()
'在子类中设置连接生成器属性
端接头
好友只读属性ConnectionStringBuilder()作为Npgsql.NpgsqlConnectionStringBuilder
得到
返回\u conStrBldr
结束
端属性
好友属性连接()作为Npgsql.NpgsqlConnection
得到
返回连接
结束
设置(ByVal值为Npgsql.NpgsqlConnection)
_连接=值
端集
端属性
'杂项属性-高级程序员信息