正文
按照上一篇結尾留下的話題,本篇著重對數據庫操作方面也就是常用工廠模式的地方來進行泛型 委托方向的使用。
一般大型項目中大家都喜歡依賴注入的方式來數據庫操作進行設計,也就是站在面向組件的層次。這裡不討論這樣設計的優缺點,我們來看下如果不適用這種方式,只使用委托和泛型如何來良好的替代上面的方案。
首先分析下sql操作有哪些元素。
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; } }
這裡只是一部分,當然也是最常用的部分。選擇抽出這些是因為大部分數據庫的驅動都支持這些。舉個數據庫連接的例子
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中並不滿足需求的場合下我們選擇自己填充,同樣的還有下面所有的SqlCommandData等,因為不是文章主旨想表達的內容就不啰嗦了。
明白這些,下面我們就開始一步一步設計,數據庫的常用操作有哪些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中,標准的command要分很多種類型,例如增刪查改。然後對於DbDataAdapter沒有一個標准的構造函數例如public SqlDataAdapter(SqlCommand selectCommand)這種形式。這樣對於不同的類型又要分開操作。既然我們要選擇最簡潔的方法,自然這樣的方式我們就先不考慮了。那麼我們把眼光再網上拋一層,以方法直接作為可變元素。
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);
然而我們並非籠統的講所有的方法都抽出,這樣也就是失去了文章本來想要表達的意思。這裡我們是將原來基礎的方法分解,抽出可憐的邏輯設計成委托。舉2個簡單的例子
public DbCommand CreateCommand(SqlCommandData sql) { DbCommand _c = doCreateCommand(sql.SqlClause); myConnection.Open(); if (IsTransaction && myTransaction == null) { myTransaction = myConnection.BeginTransaction(); } // URL:http://www.bianceng.cn/Programming/csharp/201410/45787.htm 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); PopuloateCommand(_c, sql); if (!Create) { Dispose(true); } return _d.Tables[0]; } }
那麼我們在各自的驅動類中實現這裡委托的邏輯,例如oracle的驅動中
public class OracleDriver : BaseDriver { public OracleDriver() { this.doCreateConnectionEx = () => { MaoyaDbConnection mc = new MaoyaDbConnection(); mc.ConnectionString = this.ConnectionString; mc.Create = true; mc.doCreateConnection = (conn) => { return new OracleConnection(conn); }; mc.doCreateFrontConnection = (conn) => { return this.CreateConnection<OracleConnection>(conn); }; mc.doCreateCommand = (comm) => { return new OracleCommand(comm); }; mc.doCreateDataAdapter = (sqlcomm) => { return 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; }; } }
或者在mysql的驅動中
public class MySqlDriver : BaseDriver { public MySqlDriver() { this.doCreateConnectionEx = () => { MaoyaDbConnection mc = new MaoyaDbConnection(); mc.ConnectionString = this.ConnectionString; mc.Create = true; mc.doCreateConnection = (conn) => { return new MySqlConnection(conn); }; mc.doCreateFrontConnection = (conn) => { return this.CreateConnection<MySqlConnection>(conn); }; mc.doCreateCommand = (comm) => { return new MySqlCommand(comm); }; mc.doCreateDataAdapter = (sqlcomm) => { return 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; }; } }
這麼寫似乎是ok了,但是我們發現各個驅動中還是有很多可以抽出通用的部分,那麼我們重回泛型的概念
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; }; //URL:http://www.bianceng.cn/Programming/csharp/201410/45787.htm mc.doCreateFrontConnection = (conn) => { return 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方法我們再各自的驅動類中實現即可。
題外
本篇到此結束,所示代碼僅供參考未經測試,功能也只是部分,例如事務操作都沒有闡述等等。下一篇和大家一起討論下依賴注入的一些另類實現方法。