設計模式/原則篇- Unit of Work


概念

  Unit of Work 即工作單元。 用來維護一組受業務影響的對象列表,將多個操作放在一個單元中,把操作原子化,通過事務統一完成一次提交,如果某個過程出現異常,就將所有修改進行回滾,保證數據的有效狀態。同時減少了應用程序與數據庫通信,有利於提升系統的性能。

 

具體使用(銀行領域的轉賬建模)

整體項目結構預覽

ProjectStructure

構建UnitOfWork.Infrastructure

1、新建Domain文件夾,添加IAggregateRoot接口

IAggregateRoot接口屬於聚合根,所有業務對象(Entity)都需要實現聚合根。外部對象需要訪問聚合內的實體時,只能通過聚合根進行訪問,而不能直接訪問。領域模型需要根據領域概念分成多個聚合,每個聚合都有一個實體作為聚合根,通俗的說,領域對象從無到有的創建,以及CRUD的操作都應該作用於聚合根上,而不是單獨的某個實體。

2、新建UnitofWork文件夾,添加IUnitofWorkRepository接口

void PersistCreationOf(IAggregateRoot entity);
void PersistUpdateOf(IAggregateRoot entity);
void PersistDeletionOf(IAggregateRoot entity);

3、添加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

1、新建Account類並實現IAggregateRoot接口  a.定義公共的屬性Balace(余額) b.定義帶參數(balace) 的構造函數

2、添加IAccountRepository接口,將Account持久化  定義添加/更新/刪除方法

3、新建AccoutService類 定義公共的IAccountRepository對象和IUnitOfWork對象,通過其構造器實現依賴注入

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

1、添加AccountRepository類,AccountRepository實現了IAccountRepository和IUnitofWorkRepository接口,IAccountRepository方法的實現簡單地將工作委托至Unit Of Work(傳入待持久化實體以及Repository的引用),IUnitofWorkRepository方法中實現了真正的持久化任務,具體的持久化方式可以用ADO.NET/EF/NH等。

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
    }
}

2、添加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

1、添加控制台項目用於驗證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();
}

執行后結果如下:

31B2.tmp

UML類圖

Unit of Work 

 

總結

對於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的觀點與疑問,希望大家能夠各發己見,一起探討一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM