周末了比較清閒,把自己的orm框架整理了下,開源了.
已經做出來的東西通常感覺有些簡單,一些新手或許聽到"框架"一類的詞覺得有些"高深",簡單來說orm就是把ado的封裝.
在介紹這個框架的第一篇博文,已經把DalBase介紹了一下設計思路,本篇的DBHelper對象也是給dalBase來用的,可以說框架的所有定義對象都是為了它.
這裡起名叫DBHelper,因為我也是從寫SQLHelper開始的,DBHelper只不過是所有類型對ado操作的各種方法的封裝,所以本篇博文希望給c#新手,或是對ado.net認識比較模糊的有一些幫助.
首先DBHelper定義是個抽象的,因為我不知道DalBase 到底要訪問哪種數據庫,但我知道不管哪中數據庫都會有鏈接字符串,那我就要求,必須要有連接字符串.
public DbHelperBase(string connStr) { _ConnStr = connStr; } View Code
DbHelper是個抽象的,那它的成員必然就不能有具體對象.
那就把ado.net常用的對象定義出來.(如果你是初學者,我的建議是 用到哪個對象再去定義,否則到後期自己都不知道定義它干啥呢)
protected abstract DbConnection DBConnectionObj { get; } protected abstract DbCommand DbCommandObj { get; } protected abstract DbDataAdapter DbDataAdapterObj { get; } protected DbTransaction DbTransObj; public DbConnection CurrentConnection { get { return DBConnectionObj; } }
說明一下,為什麼事務對象不是抽象的,因為事務對象始終是有DbConnection來創建的,我不用知道它具體是什麼類型.
DbConnection 對象public也是為了擴展其它功能而存在的.
定義了是否事務的變量,所有ado操作都會判斷當前是否處於事務的標記.
bool _IsTrans = false;
初學.net的朋友,應該都會有一個SQLHelper的類,我也曾經看過都大同小異,而且普遍都沒有事務的實現,如果你中槍了,那麼恭喜你,你即將會改變你sqlHelper的實現.
如下代碼:
/// <summary> /// 執行一條指定命令類型(SQL語句或存儲過程等)的SQL語句,返回所影響行數 /// </summary> public int ExecNonQuery(string sqlText, CommandType cmdType, params DbParameter[] param) { using (SqlConnection conn = new SqlConnection(_ConnStr)) { using (SqlCommand cmd = new SqlCommand(sqlText, conn)) { cmd.CommandType = cmdType; if (param != null) cmd.Parameters.AddRange(param); conn.Open(); return cmd.ExecuteNonQuery(); } } }
以上代碼看起來是沒什麼問題,但如果要啟用事務的話想想是否可以實現呢?
如果說改造一下加上事務的代碼就行的話
如下:
SqlTransaction tran = conn.BeginTransaction(); cmd.Transaction = tran;
這樣顯然是錯的,因為我們往往多條執行語句通常是分多次調用 ExecNonQuery() 方法的.
這樣的一個ado方法的封裝顯然是不合理的.
如果多個增刪改查(注意:查詢也可能在是事務裡)的方法,在一個事務裡,那麼必須是一個數據庫連接(Connection)
這就是為什麼把那三個對象定義到外面的原因之一了,最重要的原因是我需要子類去重寫它.
下面看看我的實現:
/// <summary> /// 打開連接,如果已經打開則什麼都不執行了 /// </summary> void OpenConnection() { if (DBConnectionObj.State != ConnectionState.Open) { DBConnectionObj.ConnectionString = _ConnStr; DBConnectionObj.Open(); } }
/// <summary> /// 給當前DbCommand對象賦值,並且OpenConnection(); /// </summary> void SetCommandAndOpenConnect(string sqlText, CommandType cmdType, params DbParameter[] param) { //按說賦值Connection,CommandType,是不用多次賦值的 DbCommandObj.CommandType = cmdType; DbCommandObj.Connection = DBConnectionObj; DbCommandObj.Parameters.Clear(); if (param != null) { DbCommandObj.Parameters.AddRange(param); } DbCommandObj.CommandText = sqlText; OpenConnection(); }
/// <summary> /// 執行一條指定命令類型(SQL語句或存儲過程等)的SQL語句,返回所影響行數 /// </summary> public int ExecNonQuery(string sqlText, CommandType cmdType, params DbParameter[] param) { try { SetCommandAndOpenConnect(sqlText, cmdType, param); return DbCommandObj.ExecuteNonQuery(); } catch (Exception ex) { throw ex; } finally { CloseConnect(); } }
看到這三個方法或許對於初學者會感到迷茫了,沒有看到任何有關事務的代碼呢,兜個圈子,現在想象一下如果加事務的話,需要做什麼?
我們先從理論上認識一下,事務處理的流程
1.指定事務是哪個Connection
2.Command的事務對象指定到該事務.
3.Open()
4.提交或回滾(我是能寫漢字的地方絕不寫拼音)
5.關閉連接.
繼續說這幾個方法,為什麼定義SetCommandAndOpenConnect 和 OpenConnection 這兩個方法,本著盡量減少重復代碼的原則.僅此而已.
既然Connection和Command都已經定義到方法外了,那就是說我只要再執行Command.ExecuteNonQuery()方法前,給他們賦值就行了.
也就是開始事務只需要給這個兩個對象賦值即可
事務的相關代碼如下:
/// <summary> /// 開始執行事務 /// </summary> public void TransStart() { OpenConnection(); DbTransObj = DBConnectionObj.BeginTransaction(); DbCommandObj.Transaction = DbTransObj; _IsTrans = true; } /// <summary> /// 事務提交 /// </summary> public void TransCommit() { _IsTrans = false; DbTransObj.Commit(); CloseConnect(); } /// <summary> /// 事務回滾 /// </summary> public void TransRollback() { _IsTrans = false; DbTransObj.Rollback(); CloseConnect(); }
這就是事務的方法了.
最後一個CloseConnect()方法,差點把它遺忘了
/// <summary> /// 關閉連接,如果沒有開始事務或連接打開時才關閉 /// </summary> void CloseConnect() { if (!_IsTrans) { if (DBConnectionObj.State == ConnectionState.Open) { DBConnectionObj.Close(); DBConnectionObj.Dispose(); } } }
當開始事務時,連接是不能關的.只有提交了或回滾了才會把當前連接斷掉.
到這裡其實DbHelper的設計基本完成,再貼一下關於查詢的幾個方法,和執行類似就不解釋了.
/// <summary> /// 獲得首行首列 /// </summary> public object GetScalar(string sqlText, CommandType cmdType, params DbParameter[] param) { try { SetCommandAndOpenConnect(sqlText, cmdType, param); return DbCommandObj.ExecuteScalar(); } catch (Exception ex) { throw ex; } finally { CloseConnect(); } } /// <summary> /// 執行一條SQL語句返回DataSet對象 /// </summary> public DataSet GetDataSet(string sqlText, CommandType cmdType, params DbParameter[] param) { try { SetCommandAndOpenConnect(sqlText, cmdType, param); DbDataAdapterObj.SelectCommand = DbCommandObj; DataSet ds = new DataSet(); DbDataAdapterObj.Fill(ds); return ds; } catch (Exception ex) { throw ex; } finally { CloseConnect(); } } /// <summary> /// 獲得DataReader對象 /// </summary> public DbDataReader GetDataReader(string sqlText, CommandType cmdType, params DbParameter[] param) { try { SetCommandAndOpenConnect(sqlText, cmdType, param); CommandBehavior cmdBehavior = CommandBehavior.CloseConnection; if (_IsTrans) { cmdBehavior = CommandBehavior.Default; } DbDataReader dbReader = DbCommandObj.ExecuteReader(cmdBehavior); return dbReader; } catch (Exception ex) { throw ex; } finally { //DataReader用dbReader對象來關閉 //CloseConnect(); } }
這裡需要注意的是關於返回DataReader對象時不能關閉Connect,和 cmdBehavior 的賦值.
好了,到這DBHelper的設計和核心代碼已經全部實現了.
試想一下我現在要完成SQLServerHelper的實現需要做的是什麼?
當然只要實現父類的那幾個抽象屬性就行了.
代碼如下:
public class SQLHelper : DbHelperBase { public SQLHelper(string connStr) : base(connStr) { } SqlConnection _DBConnectionObj; SqlCommand _DbCommandObj; SqlDataAdapter _DbDataAdapterObj; protected override DbConnection DBConnectionObj { get { //SqlBulkCopy aa = new SqlBulkCopy(new SqlConnection()); if (_DBConnectionObj == null) { _DBConnectionObj = new SqlConnection(_ConnStr); } return _DBConnectionObj; } } protected override DbCommand DbCommandObj { get { if (_DbCommandObj == null) { _DbCommandObj = new SqlCommand(); } return _DbCommandObj; } } protected override DbDataAdapter DbDataAdapterObj { get { if (_DbDataAdapterObj == null) { _DbDataAdapterObj = new SqlDataAdapter(); } return _DbDataAdapterObj; } } } View CodeOracleHelper,oledbhelper,SQLiteHelper,就不貼代碼了.
轉載建議標明出處(我從來不強迫別人做我管不了的事).
整個框架的源碼在介紹框架的第一篇博文裡有.
歡迎吐槽,點贊,建議,批評,指正,抄襲.