承接上篇主題,本篇著重於資料庫操作層面(常用工廠模式處),探討如何以泛型與委派良好替代傳統依賴注入設計。不討論依賴注入優劣,專注於委派與泛型的實作策略。
常用資料庫驅動支援的核心物件:
public class BaseDriverParam {
public DbCommand baseCommand { get; set; }
public DbConnection baseConnection { get; set; }
public DataAdapter baseDataAdapter { get; set; }
public DataParameter baseDataParameter { get; set; }
public DbTransaction baseDbTransaction { get; set; }
public DbDataReader baseDbDataReader { get; set; }
}
各資料庫連線類別皆繼承 DbConnection:
public sealed class OleDbConnection : DbConnection, ICloneable, IDbConnection, IDisposable public sealed class MySqlConnection : DbConnection, ICloneable public sealed class SqlConnection : DbConnection, ICloneable public sealed class SQLiteConnection : DbConnection, ICloneable public sealed class OracleConnection : DbConnection, ICloneable
特殊需求(如 DataParameter 不滿足 IDbDataParameter)可自行擴充。
常用操作:doCreateConnection、doCreateCommand、doCreateDataAdapter、doFillCommand 等。
public class TBaseDriver<TCommand, TDbConnection, TDataAdapter, TDataParameter, TDbTransaction> : BaseDriver where TCommand : DbCommand, new() where TDbConnection : DbConnection, new() where TDataAdapter : DbDataAdapter, new() where TDbTransaction : DbTransaction, new() where TDataParameter : DataParameter, new()
問題:DbDataAdapter 建構子不統一(如 SqlDataAdapter(SqlCommand)),不同命令類型(增刪查改)需分開處理,不夠簡潔。
public delegate string ActionDelegate(); public delegate IConnectionEx CreateConnectionExDelegate(); public delegate DbCommand CreateCommandDelegate(string dbClause); public delegate DbConnection CreateConnectionDelegate(string dbConnection); public delegate DbConnection CreateFrontConnectionDelegate(string dbConnection); public delegate DbCommand FillCommandDelegate(DbCommand dbCommand, SqlCommandData sqlCD); public delegate DataAdapter CreateDataAdapter(DbCommand dbCommand);
將原本基礎方法分解,抽出核心邏輯設計成委派。範例:
public DbCommand CreateCommand(SqlCommandData sql) {
DbCommand _c = doCreateCommand(sql.SqlClause);
myConnection.Open();
if (IsTransaction && myTransaction == null)
myTransaction = myConnection.BeginTransaction();
if (IsTransaction) {
if (myTransaction == null) myTransaction = myConnection.BeginTransaction();
_c.Transaction = myTransaction;
}
_c.Connection = myConnection;
_c.CommandTimeout = 300;
_c.CommandType = sql.CommandType;
_c = doFillCommand(_c, sql);
return _c;
}
public DataTable Query(SqlCommandData sql) {
using (DbCommand _c = this.CreateCommand(sql)) {
DataAdapter _s = doCreateDataAdapter(_c);
DataSet _d = new DataSet();
_s.Fill(_d);
PopulateCommand(_c, sql);
if (!Create) Dispose(true);
return _d.Tables[0];
}
}
OracleDriver:
public class OracleDriver : BaseDriver {
public OracleDriver() {
this.doCreateConnectionEx = () => {
MaoyaDbConnection mc = new MaoyaDbConnection();
mc.ConnectionString = this.ConnectionString;
mc.Create = true;
mc.doCreateConnection = (conn) => new OracleConnection(conn);
mc.doCreateFrontConnection = (conn) => this.CreateConnection<OracleConnection>(conn);
mc.doCreateCommand = (comm) => new OracleCommand(comm);
mc.doCreateDataAdapter = (sqlcomm) => new OracleDataAdapter((OracleCommand)sqlcomm);
mc.doFillCommand = (sqlcomm, sql) => {
foreach (DataParameter dp in sql.Parameters) {
OracleParameter p = new OracleParameter();
p.ParameterName = dp.ParameterName;
p.Size = dp.Size;
p.Direction = dp.Direction;
p.IsNullable = dp.IsNullable;
p.Value = dp.Value == null ? DBNull.Value : dp.Value;
sqlcomm.Parameters.Add(p);
}
return sqlcomm;
};
return mc;
};
}
}
MySqlDriver:
public class MySqlDriver : BaseDriver {
public MySqlDriver() {
this.doCreateConnectionEx = () => {
MaoyaDbConnection mc = new MaoyaDbConnection();
mc.ConnectionString = this.ConnectionString;
mc.Create = true;
mc.doCreateConnection = (conn) => new MySqlConnection(conn);
mc.doCreateFrontConnection = (conn) => this.CreateConnection<MySqlConnection>(conn);
mc.doCreateCommand = (comm) => new MySqlCommand(comm);
mc.doCreateDataAdapter = (sqlcomm) => new MySqlDataAdapter((MySqlCommand)sqlcomm);
mc.doFillCommand = (sqlcomm, sql) => {
foreach (DataParameter dp in sql.Parameters) {
MySqlParameter p = new MySqlParameter();
p.ParameterName = dp.ParameterName;
p.Size = dp.Size;
p.Direction = dp.Direction;
p.IsNullable = dp.IsNullable;
p.Value = dp.Value == null ? DBNull.Value : dp.Value;
sqlcomm.Parameters.Add(p);
}
return sqlcomm;
};
return mc;
};
}
}
public class TBaseDriver<TCommand, TDbConnection, TDataAdapter, TDataParameter, TDbTransaction> : BaseDriver
where TCommand : DbCommand, new()
where TDbConnection : DbConnection, new()
where TDataAdapter : DbDataAdapter, new()
where TDbTransaction : DbTransaction, new()
where TDataParameter : DataParameter, new() {
public TBaseDriver() {
this.doCreateConnectionEx = () => {
MaoyaDbConnection mc = new MaoyaDbConnection();
mc.ConnectionString = this.ConnectionString;
mc.Create = true;
mc.doCreateConnection = (conn) => {
var baseConn = new TDbConnection();
baseConn.ConnectionString = conn;
return baseConn;
};
mc.doCreateFrontConnection = (conn) => this.CreateConnection<TDbConnection>(conn);
mc.doCreateCommand = (comm) => {
var baseComm = new TCommand();
baseComm.CommandText = comm;
return baseComm;
};
mc.doFillCommand = (sqlcomm, sql) => {
foreach (DataParameter dp in sql.Parameters) {
TDataParameter p = new TDataParameter();
p.ParameterName = dp.ParameterName;
p.Size = dp.Size;
p.Direction = dp.Direction;
p.IsNullable = dp.IsNullable;
p.Value = dp.Value == null ? DBNull.Value : dp.Value;
sqlcomm.Parameters.Add(p);
}
return sqlcomm;
};
return mc;
};
}
}
共通邏輯(連線、命令、參數填充)提取至泛型基底類別;doCreateDataAdapter 因各資料庫差異大,保留在具體驅動類別實作。
本篇示範如何結合泛型與委派簡化多資料庫驅動開發,代碼僅供參考未經測試,功能為部分實作(如交易處理未闡述)。下一篇將探討依賴注入的另類實作方法。