Unit of Work 即工做單元。 用來維護一組受業務影響的對象列表,將多個操做放在一個單元中,把操做原子化,經過事務統一完成一次提交,若是某個過程出現異常,就將全部修改進行回滾,保證數據的有效狀態。同時減小了應用程序與數據庫通訊,有利於提高系統的性能。web
總體項目結構預覽
構建UnitOfWork.Infrastructure
一、新建Domain文件夾,添加IAggregateRoot接口數據庫
IAggregateRoot接口屬於聚合根,全部業務對象(Entity)都須要實現聚合根。外部對象須要訪問聚合內的實體時,只能經過聚合根進行訪問,而不能直接訪問。領域模型須要根據領域概念分紅多個聚合,每一個聚合都有一個實體做爲聚合根,通俗的說,領域對象從無到有的建立,以及CRUD的操做都應該做用於聚合根上,而不是單獨的某個實體。api
二、新建UnitofWork文件夾,添加IUnitofWorkRepository接口函數
void PersistCreationOf(IAggregateRoot entity); void PersistUpdateOf(IAggregateRoot entity); void PersistDeletionOf(IAggregateRoot entity);三、添加IUnitofWork接口 注意在添加/修改/刪除的方法中傳入IUnitofWorkRepository,在提交時Unit Of Work將真正持久化工做放在IUnitofWorkRepository的具體類實現中進行性能
void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository); void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository); void RegisterRemove(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository) void Commit();
構建UnitOfWork.Model
一、新建Account類並實現IAggregateRoot接口 a.定義公共的屬性Balace(餘額) b.定義帶參數(balace) 的構造函數this
二、添加IAccountRepository接口,將Account持久化 定義添加/更新/刪除方法spa
三、新建AccoutService類 定義公共的IAccountRepository對象和IUnitOfWork對象,經過其構造器實現依賴注入code
public IAccountRepository _accountRepository; public IUnitOfWork _unitOfWork; public AccoutService(IAccountRepository accountRepository, IUnitOfWork unitOfWork) { _accountRepository = accountRepository; _unitOfWork = unitOfWork; } /// <summary> /// 轉帳 /// </summary> /// <param name="from"></param> /// <param name="to"></param> /// <param name="account"></param> public void Transfer(Account from ,Account to ,decimal balace) { if (from.Balace > balace) { from.Balace -= balace; to.Balace += balace; _accountRepository.Save(from); _accountRepository.Save(to); _unitOfWork.Commit(); } }
構造UnitOfWork.Repository
一、添加AccountRepository類,AccountRepository實現了IAccountRepository和IUnitofWorkRepository接口,IAccountRepository方法的實現簡單地將工做委託至Unit Of Work(傳入待持久化實體以及Repository的引用),IUnitofWorkRepository方法中實現了真正的持久化任務,具體的持久化方式能夠用ADO.NET/EF/NH等。orm
public class AccountRepository:IAccountRepository ,IUnitOfWorkRepository { private IUnitOfWork _unitOfWork; public AccountRepository(IUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; } public void Save(Account account) { _unitOfWork.RegisterAdd(account,this); } public void Update(Account account) { _unitOfWork.RegisterUpdate(account,this); } public void Remove(Account account) { _unitOfWork.RegisterRemove(account,this); } public void PersistCreationOf(Infrastructure.Domain.IAggregateRoot entity) { //ADO.NET/EF/NH } public void PersistUpdateOf(Infrastructure.Domain.IAggregateRoot entity) { //ADO.NET/EF/NH } public void PersistDeletionOf(Infrastructure.Domain.IAggregateRoot entity) { //ADO.NET/EF/NH } }二、添加NHUnitOfWork類,實現IUnitofWork接口對象
public class NHUnitOfWork : IUnitOfWork { public Dictionary<IAggregateRoot, IUnitOfWorkRepository> addedEntitys; public Dictionary<IAggregateRoot, IUnitOfWorkRepository> changedEntitys; public Dictionary<IAggregateRoot, IUnitOfWorkRepository> deletedEntitys; public NHUnitOfWork() { addedEntitys = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>(); changedEntitys = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>(); deletedEntitys = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>(); } public void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository) { if (!changedEntitys.ContainsKey(entity)) { changedEntitys.Add(entity,unitOfWorkRepository); } } public void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository) { if (!addedEntitys.ContainsKey(entity)) { addedEntitys.Add(entity,unitOfWorkRepository); } } public void RegisterRemove(IAggregateRoot entity, IUnitOfWorkRepository unitOfWorkRepository) { if (!deletedEntitys.ContainsKey(entity)) { deletedEntitys.Add(entity,unitOfWorkRepository); } } public void Commit() { using (TransactionScope scope = new TransactionScope()) { foreach (IAggregateRoot entity in this.addedEntitys.Keys) { this.addedEntitys[entity].PersistCreationOf(entity); } foreach (IAggregateRoot entity in this.changedEntitys.Keys) { this.changedEntitys[entity].PersistUpdateOf(entity); } foreach (IAggregateRoot entity in this.deletedEntitys.Keys) { this.deletedEntitys[entity].PersistDeletionOf(entity); } scope.Complete(); } } }NHUnitOfWork類使用3個字典變量來記錄對業務實體的代執行修改。addedEntitys 對應於被添加到數據存儲的實體,changedEntitys 處理更新的實體,deletedEntitys處理實體刪除,與字典中的實體鍵匹配的IUnitOfWorkRepository將被保存下來,並用於 Commit 方法之中 ,來調用Repository對象,該對象包含真正持久化實體的代碼 。Commit方法遍歷每個字典,並調用相應的IUnitOfWorkRepository方法(傳遞實體引用)。Commit方法中的工做均被 TransactionScope 代碼包裝起來,若是在IUnitOfWorkRepository中執行任務時出現異常,則全部工做回滾,數據存儲將保持原來的狀態。
構建UnitOfWork.Console
一、添加控制檯項目用於驗證UnitofWork模式效果
static void Main(string[] args) { Account a = new Account(1000); System.Console.WriteLine("如今a,存有{0}",a.Balace); Account b = new Account(200); System.Console.WriteLine("如今b,存有{0}",b.Balace); System.Console.WriteLine("a轉給b 500元,開始轉帳......"); IUnitOfWork unitOfWork = new NHUnitOfWork(); IAccountRepository accountRepository = new AccountRepository(unitOfWork); AccoutService service = new AccoutService(accountRepository,unitOfWork); service.Transfer(a,b,500); System.Console.WriteLine("轉帳結束"); System.Console.WriteLine("a當前金額:{0}",a.Balace); System.Console.WriteLine("b當前金額:{0}",b.Balace); System.Console.ReadKey(); }執行後結果以下:
對於Unit of Work模式是一種解決方案,一種思路,每一個項目的環境都是不同的,因此咱們須要理解的就是其中的原理。以上實例主要是講解如何部署UnitofWork模式,便於你們的理解,謝謝!
疑問討論
上面講的是網上資料中最廣泛的說法,對經典的Unit of Work模式進行了說明。可是我我的有一個觀點須要和你們探討一下:
先說一個問題,你們在初次接觸Unit of Work的時候,會不會有一個疑問——這個和數據庫事務有什麼區別?
而後我再說個人觀點吧。
1.我以爲Unit of Work就是一個思想,工做單元及原子性操做,而這個關聯到數據庫中,也就是事務了,因此我以爲數據庫事務是Unit of Work在數據庫中的一種體現。
2.上面講述的Unit of Work是網上最多見的一種說法。我我的以爲,上面那只是一種實現方式,而在網上看到大部分關於Unit of Work的優勢中「減小數據庫鏈接、減小數據庫鏈接時間」,這些都是這種實現方式的優勢。可是我認爲這並非Unit of Work真正的意圖,我認爲Unit of Work只是單純的原子操做思想,就像我認爲微軟提供的TransactionScope也是Unit of Work的一種實現同樣。
3.我認爲上述的這種廣泛的說法可能存在這樣的問題:
前置條件:數據庫表主鍵爲自增的int型ID。
場景: A表一條數據執行了修改操做,B表執行了一個新增操做,接下來有一個if判斷,一條分支是拿B表新增數據的ID調用一個webapi,在以後還有一個對C表的修改操做,另外一條分支是在D表中新增一條數據。
問題:由於全部的增刪改操做都會被延後到工做單元結束的地方進行執行,因此在代碼執行到往B表插入數據的時候,實際上沒有插入,這時若是在if判斷的地方走了第一個分支,用B表插入的數據ID去調用webapi,這將會出現問題。
解決方案:
1.不適用自增ID,好比換成GUID;
2.爲webapi實現一個上述的Unit of Work的適配器,將api操做也壓入集合中,等到後面一塊兒處理;
3.在工做單元開始的時候就開啓一個數據庫事務。
第一個解決方案在一些成型系統中再遇到就是不可行的,第二個方案會稍顯麻煩,第三個方案就不存在以前說的「減小數據庫鏈接時間」的優勢了。
以上是我我的對Unit of Work的觀點與疑問,但願你們可以各發己見,一塊兒探討一下。