Unit Of Work--工作單元
簡介
最近忙著新專案的架構,已經有一段時間沒有更新部落格了,一直考慮著要寫些什麼,直到有一天跟朋友談起他們公司開發資料層遇到的一些問題時,我想應該分享一些專案中使用的資料訪問模式。
雖然最近一直都在使用Go語言開發資料伺服器,但是本篇文章用到的語言仍然是C#,文章內提供的程式碼僅僅是分享如何使用工作單元,至於如何將這個模式引入到專案中去,就需要各位自己去實現了,畢竟每個專案都是不一樣的,需要根據專案具體的環境來進行組合。
本篇文章包括以下內容:
- 什麼是工作單元
- 基於ADO.NET的實現
- 總結
什麼是工作單元
該模式用來維護一個由已經被業務事務修改(CRUD除了R)的業務物件組成的列表並負責協調這些修改的持久化工作以及所有標記的併發問題。
在web應用中,由於每個使用者的請求都是屬於不同執行緒的,需要保持每次請求的所有資料操作都成功的情況下提交資料,只要有一個失敗的操作,則會對使用者的此次請求的所有操作進行回滾,以確保使用者操作的資料始終處於有效的狀態。
基於ADO.NET的實現
在不使用任何資料層框架,僅僅使用ADO.NET的情況下,一般流程的方式如下:
- 例項化IDbConnection
- 然後例項化IDbCommand
- 設定IDbCommand的Text和Parameters
- 執行IDbCommand.ExecuteNoQuery
- 釋放IDbCommand、IDbConnection
一般情況下,我們會給每個資料庫表建立對應的資料庫互動類並提供CRUD方法,除了R以外,其他的方法都會實現以上的流程。
然而如果使用者一次請求會進行多次CUD操作的情況下,只能在使用者開始進行資料操作之前使用TransactionScope將多次操作包裹在內,這樣實現相對麻煩的。
其實我們可以將使用者進行的CUD的SQL語句與引數現在儲存起來,到最後再一併進行提交,有點類似儲存過程的樣子,這其實就是工作單元模式了,首先建立一個用來儲存SQL語句和引數的類,程式碼如下:
classSQLEntity{privatestring m_SQL =null;publicstring SQL {get{return m_SQL;}}privateIDataParameter[] m_Parameters=null;publicIDataParameter[]Parameters{get{return m_Parameters;}}publicEntity(string sql,paramsIDataParameter[] parameters){this.m_SQL = sql;this.m_Parameters = parameters;}}
如果將所有CUD的操作都放在一個List<SQLEntity>內,在需要提交的時候只需要遍歷List<SQLEntity>內的元素,並通過重複開始的流程就行(這裡需要稍微重構一下),程式碼如下:
classSQLUnitOfWork{privateIDbConnection m_connection =null;privateList m_operations =newList();publicSQLUnitOfWork(IDbConnection connection){this.m_connection = connection;}publicvoidRegisterAdd(string sql,paramsIDataParameter[] parameters){this.m_operations.Add(newSQLEntity(sql, parameters));}publicvoidRegisterSave(string sql,paramsIDataParameter[] parameters){this.m_operations.Add(newSQLEntity(sql, parameters));}publicvoidRegisterRemove(string sql,paramsIDataParameter[] parameters){this.m_operations.Add(newSQLEntity(sql, parameters));}publicvoidCommit(){using(IDbTransaction trans =this.m_connection.BeginTransaction()){try{using(IDbCommand cmd =this.m_connection.CreateCommand()){ cmd.Transaction= trans; cmd.CommandType=CommandType.Text;this.ExecuteQueryBy(cmd);} trans.Commit();}catch(Exception ex){ trans.Rollback();}}}privatevoidExecuteQueryBy(IDbCommand cmd){foreach(var entity inthis.m_operations){ cmd.CommandText= entity.SQL; cmd.Parameters.Clear();foreach(var parameter in entity.Parameters){ cmd.Parameters.Add(parameter);} cmd.ExecuteNonQuery();}this.m_operations.Clear();}}
有了以上的SQLUnitOfWork,我們的資料庫類在呼叫CUD方法的時候,就只要呼叫相應RegisterXXX方法了,資料層實現程式碼如下:
classSchoolRepository{privateSQLUnitOfWork m_uow =null;publicSchoolRepository(SQLUnitOfWork uow){this.m_uow = uow;}publicvoidAdd(School school){this.m_uow.RegisterAdd("insert school values(@id, @name)",newIDbDataParameter[]{newSqlParameter("@id", school.Id),newSqlParameter("@name", school.Name)});}publicvoidSave(School school){this.m_uow.RegisterSave("update school set name = @name where id = @id",newIDbDataParameter[]{newSqlParameter("@id", school.Id),newSqlParameter("@name", school.Name)});}publicvoidRemove(School school){this.m_uow.RegisterRemove("delete from school where id = @id",newIDbDataParameter[]{newSqlParameter("id", school.Id)});}}classStudentRepository{//程式碼類似,因此省略}
有了以上的準備,再使用以下的程式碼來看看工作單元的效果,程式碼如下:
SQLUnitOfWork uow =newSQLUnitOfWork(connection);SchoolRepository schoolRepositry =newSchoolRepository(uow);StudentRepository studentRepository =newStudentRepository(uow);School school =newSchool{Id=Guid.NewGuid().ToString(),Name="一中",}; schoolRepositry.Add(school);for(int i =0; i <7; i++){Student student =newStudent{Id=Guid.NewGuid().ToString(),Name=string.Format("學生{0}號", i),Age=7+ i,SchoolId= school.Id}; studentRepository.Add(student);} school.Name="二中"; schoolRepositry.Save(school); uow.Commit();
接著會看到資料庫中會看到圖中的資料,如圖:
總結
這樣就完成了基於ADO.NET的簡單工作單元實現了,可能有些人會有疑問,跟其他人實現的工作單元有很多不同的地方,這是因為該版本只是一個大致思路的實現,並沒有跟其他框架、庫的結合,為了能使其他人理解工作單元的原理,因此實現相對比較簡單。
由於模式是一種解決方案,一種思路,每個專案的環境、功能、結構都不一樣,因此實現的方式會有不同。對於模式不能死記硬背,要理解其中的原理,可以通過自我實踐或者參考他人的程式碼來實現,如果每個專案都是照抄模式的話,那麼就失去了它該有的作用了。
那麼文章就到這裡了,如果有問題和疑問歡迎留言,程式碼僅供參考請勿應用到實際專案中,謝謝。