General multi-level data access pattern?

I played with some new n-level data access templates and came across one that seems very flexible and easy to implement. Basically, I needed a solution that could force various data layers to connect / swapabale on the fly - i.e. Access to basic data from the database, distributed caching, local caching, etc.

The code below is easy to reuse and incredibly efficient - just a few ticks longer than my previous fully hard coded solution.

What does it look like? Is there any way to improve this? Any general thoughts or criticisms? Any input from anyone using similar templates?

Base class:

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 implementation:

 // 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"; } } 

Data Access:

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

Using the factory above, any combination of basic adapters and extenders (Extend <> () should be called in execution order) can be easily and easily used on the fly through the interface, and the business layer knows nothing of the implementation.

In this case above, calling GetString () will result in "SQL Distributed Cache, Local Cache". In a real-world scenario, the local cache will be called first. If there is no element, we will move on to the distributed cache, and if it is not there, we will get it from the database - and any module can be replaced or removed as necessary depending on the obejct, user, etc.

+2
source share
3 answers

I would look at http://en.wikipedia.org/wiki/Decorator_pattern for this - your example would then become something like this:

 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
source

What you do looks reasonably reasonable, although it would be useful to see how classes are related in terms of what namespaces and assemblies they are.

My experience is in abstracting data providers behind interfaces, and data providers (and sometimes interfaces) live in a separate assembly (like this: 1 for BL, 1 for interfaces and 1 for each of the data providers).

I define interfaces around business concepts (for example, IPageDataProvider, ICustomerDataProvider, etc.) and not data sources (i.e. db tables), and you want to remember the principle of interface segeration when developing interfaces.

I load the required data provider at runtime through the factory; this uses the Activator.CreateInstance method. factory receives instructions from config.

So, when I want to consume data in my business logic, I create an instance of the desired implementation of the interface through the factory (one line of code).

With this approach, there is no base class that the providers have , but obviously you could use the base classes in the data provider if you want.

0
source

I came up with a kind of hybrid abstract factory that can serve your purpose, and also hides the connection details from the developers. It provides a set of connections, in our case we needed 4 in each set. These four will be returned by the factory “connection set”, there is also a “custom factory connection set, where you can change the connections returned at any time. You use a proxy to access the connection objects. Then I access the proxy server through one single. so I can set it in the application load event or global.asmx, then it’s very easy to swap which connections you use. Although you can swap places. Hope this helps.

This is specifically written for my script, so maybe a little too much for you?

Note that this is for npgsql, you can easily change it to standard .data. client base classes or sqlclent classes ...

  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 
0
source

Source: https://habr.com/ru/post/888108/


All Articles