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.