I've try it on .NET framework 2.0. IDBConnection, IDBTransaction and IDBDataParameter are available in System.Data namespace since .NET framework 1.1 however, DBDataReader in System.Data.Common was available not until .NET framework 2.0 (DBDataReader was derived by SqlDataReader, OracleDataReader and OleDbDataReader). Those are the important interfaces and class needed.
Basically we need to create an abstract layer to our db helper. Let's just call it IDBHelper. Define all basic methods that can support various databases. It's important not to have a method that is specific to any data provider such as ExecuteXmlReader.
...For concrete implementation. Create a class that implement IDBHelper interface. I named the class as SqlClientHelper.
using System.Data;
using System.Data.Common;
public interface IDbHelper
{
IDbConnection CreateConnectionInstance(string connectionString);
...
DbDataReader ExecuteReader(IDbConnection connection, string query, CommandType cmdType, params IDbDataParameter[] commandParameters);
...
IDbDataParameter CreateParam(string paramName, object paramValue);
}
...Then, the code for OracleClientHelper.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
public class SqlClientHelper : IDbHelper
{
public IDbConnection CreateConnectionInstance(string connectionString)
{
return new SqlConnection(connectionString);
}
...
public DbDataReader ExecuteReader(IDbConnection connection, string query, CommandType cmdType, params IDbDataParameter[] commandParameters)
{
...
}
...
public IDbDataParameter CreateParam(string paramName, object paramValue)
{
SqlParameter param = new SqlParameter(paramName, paramValue);
...
}
...
}
...Noticed that the instantiation of connection and parameter object has been done in the class itself. Ideally, if we don't use any specific data provider namespaces in DAL layer, it should be flexible enough to use any database without the need to change the code logic. The concern is more on the abstract layer method signature.
using System;
using System.Data;
using System.Data.OracleClient;
using System.Data.Common;
public class OracleClientHelper : IDbHelper
{
public IDbConnection CreateConnectionInstance(string connectionString)
{
return new OracleConnection(connectionString);
}
...
public DbDataReader ExecuteReader(IDbConnection connection, string query, CommandType cmdType, params IDbDataParameter[] commandParameters)
{
...
}
...
public IDbDataParameter CreateParam(string paramName, object paramValue)
{
OracleParameter param = new OracleParameter(paramName, paramValue);
...
}
...
}
Create an object factory that'll return the helper instance. This is the only place that we need to change if the project owner aka our client suddenly decided to use different data provider in future (reality is cruel).
...The code at DAL
public static class SomeObjectFactory
{
public static IDBHelper GetHelperInstance()
{
return new SqlClientHelper();
// uncomment the code below for oracle client
// return new OracleClientHelper();
}
}
...This way, adding System.Data and System.Data.Common namespace should be sufficient enough to our DAL.
using (IDbConnection connection = SomeObjectFactory.GetHelperInstance().CreateConnectionInstance(connectionString))
{
connection.Open();
string query = "SELECT * FROM tblSomething WHERE Id = @Id";
IDbDataParameter param = MyObjectFactory.GetDBHelperInstance().CreateParam("@Id", id);
DBDataReader reader = SomeObjectFactory.GetHelperInstance().ExecuteReader(connection, query, CommandType.Text, param);
...
}
...
wow! never think abt it before. really helpful
ReplyDelete