在開始UnitOfWork模式之前有必要回顧下我們耳熟能詳的Data Access Object(DAO)模式,即數據訪問對象。DAO是一種簡單的模式,我們構建應用的時候經常會使用到它,它的功能就是將DAL元素從應用程序中分離出來,在經典的三層架構中,我們會將數據持久化工作單獨分離出來,封裝成DAL層。但是,DAO並沒有隱藏它面對是一張張數據表,而且通常情況我們會為數據庫中的每一張表創建一個DAO類,想必大家對這種方式的極度的不爽了。
由於DAO模式與數據表是一對一匹配的關系,因此DAO模式很好的配合了Active Record和Transaction Script業務模式,尤其是Table Module。正因為這種與數據表一對一匹配關系,使我對DAO模式深惡痛絕。
Unit Of Work模式,即工作單元,它是一種數據訪問模式。它是用來維護一個由已經被業務修改(如增加、刪除和更新等)的業務對象組成的列表。它負責協調這些業務對象的持久化工作及並發問題。那它是怎么來維護的一系列業務對象組成的列表持久化工作的呢?通過事務。Unit Of Work模式會記錄所有對象模型修改過的信息,在提交的時候,一次性修改,並把結果同步到數據庫。 這個過程通常被封裝在事務中。所以在DAL中采用Unit Of Work模式好處就在於能夠確保數據的完整性,如果在持有一系列業務對象(同屬於一個事務)的過程中出現問題,就可以將所有的修改回滾,以確保數據始終處於有效狀態,不會出現臟數據。
在這里我們,使用一個簡單的銀行領域對兩個帳號之間的轉賬進行舉例
首先如圖進行分層搭建基礎框架
總共分為四層依次是: ---> 引用關系
UnitOfWork.Console --->UnitOfWork.Infrastructure、UnitOfWork.Model、UnitOfWork.Repository
UnitOfWork.Infrastructure
UnitOfWork.Model --->UnitOfWork.Infrastructure
UnitOfWork.Repository --->UnitOfWork.Model、UnitOfWork.Infrastructure
這不是經典的領域驅動架構,因為業務簡單就進行簡單搭建。
UnitOfWork.Infrastructure
首先,在UnitOfWork.Infrastructure建了一個Domain文件夾,里面只建了一個IAggregateRoot接口,Unit Of Work操作的實體必須是實現IAggregateRoot接口的。
/// <summary>
/// 標識接口,定義聚合根
/// </summary>
public class IAggregateRoot
{
}
建立UnitOfWork文件夾,在里面添加一個IUnitOfWorkRepository接口
public interface IUnitOfWorkRepository
{
//新增
void PersistCreationOf(IAggregateRoot entity);
//更新
void PersistUpdateOf(IAggregateRoot entity);
//刪除
void PersistDeletionOf(IAggregateRoot entity);
}
我們再向UnitOfWork文件夾中添加一個IUnitOfWork接口,IUnitOfWork接口在注冊更新、新增和刪除時,需要IUnitOfWorkRepository,這樣再提交Commit,UnitOfWork可以將真正持久化的工作委托給適合的具體實現對象,其實就是將持久化工作交給了IUnitOfWorkRepository的實現類,我們稍后看看IUnitOfWork的實現類你就清楚了。
public interface IUnitOfWork
{
/// <summary>
/// 更新
/// </summary>
/// <param name="entity"></param>
/// <param name="unitofWorkRepository"></param>
void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
/// <summary>
/// 新增
/// </summary>
/// <param name="entity"></param>
/// <param name="unitofWorkRepository"></param>
void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
/// <summary>
/// 刪除
/// </summary>
/// <param name="entity"></param>
/// <param name="unitofWorkRepository"></param>
void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository);
/// <summary>
/// 提交
/// </summary>
void Commit();
}
順便說一句,在UnitOfWork.Infrastructure中還建立了Common和Logging文件夾,Infrastructure意為基礎設施層,我們可以將通用的組件放到這里面,比如日志組件,郵件組件等。
好了,回顧下,我們在UnitOfWork.Infrastructure中其實什么業務都沒有處理,只定義了三個接口。
UnitOfWork.Model
接下來,說說這層干了什么事吧!這一層主要是處理兩個賬戶之間轉賬的業務。
向UnitOfWork.Model中添加一個Account類,實現IAggregateRoot接口,表示它是一個聚合根,可以被Unit Of Work操作。
/// <summary>
/// 賬戶類,表示Account是聚合根
/// </summary>
public class Account : IAggregateRoot
{
public Account(decimal balance)
{
Balance = balance;
}
/// <summary>
/// 賬戶余額
/// </summary>
public decimal Balance { get; set; }
}
為了持久化Account類,我們添加一個僅包含了示例有關的的Repository接口,即IAccountRepository,只是簡單的示例。
/// <summary>
/// 定義持久化操作
/// </summary>
public interface IAccountRepository
{
/// <summary>
/// 更新
/// </summary>
/// <param name="account"></param>
void Save(Account account);
/// <summary>
/// 新增
/// </summary>
/// <param name="account"></param>
void Add(Account account);
/// <summary>
/// 刪除
/// </summary>
/// <param name="account"></param>
void Remove(Account account);
}
為了完成轉賬的業務,我們需要創建一個服務類來協調兩個賬戶之間的轉賬工作,如AccountService類,在AccountService類中,通過構造函數初始化了IAccountRepository和IUnitOfWork。在完成轉賬后,它們都調用了賬戶Repository進行持久化。最后,通過Unit Of Work的Commit來確保該筆交易的完成。
public class AccountService
{
private IAccountRepository _accountRepository;
private IUnitOfWork _unitOfWork;
public AccountService(IAccountRepository accountRepository, IUnitOfWork unitOfWork)
{
_accountRepository = accountRepository;
_unitOfWork = unitOfWork;
}
/// <summary>
/// 轉賬
/// </summary>
/// <param name="from"></param>
/// <param name="to"></param>
/// <param name="amount"></param>
public void Transfer(Account from, Account to, decimal amount)
{
if (from.Balance >= amount)
{
from.Balance -= amount;
to.Balance += amount;
_accountRepository.Save(from);
_accountRepository.Save(to);
_unitOfWork.Commit();
}
}
}
總結下,在UnitOfWork.Model中我們定義的一個Account類,定義了一個持久化Account類的接口,以及轉賬業務的完成。下面我們要進入UnitOfWork.Repository探究Repository和Unit Of Work之間是怎么交互的?
UnitOfWork.Repository
在UnitOfWork.Repository,添加了一個NHUnitOfWork類,實現了UnitOfWork.Infrastructure.UnitOfWork中的IUnitOfWork接口,為什么定義到這里。因為在項目開發中你可能有NHbernator的Repository和EF的Repository。還記得我在講解IUnitOfWork接口時,曾說過這樣一句話“IUnitOfWork將持久化工作交給了IUnitOfWorkRepository的實現類”,在這里你就會找到答案了。
public class NHUnitOfWork : IUnitOfWork
{
private Dictionary<IAggregateRoot, IUnitOfWorkRepository> addedEntities;
private Dictionary<IAggregateRoot, IUnitOfWorkRepository> changedEntities;
private Dictionary<IAggregateRoot, IUnitOfWorkRepository> deletedEntities;
public NHUnitOfWork()
{
addedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
changedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
deletedEntities = new Dictionary<IAggregateRoot, IUnitOfWorkRepository>();
}
public void RegisterUpdate(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
{
if (!changedEntities.ContainsKey(entity))
{
changedEntities.Add(entity, unitofWorkRepository);
}
}
public void RegisterAdd(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
{
if (!addedEntities.ContainsKey(entity))
{
addedEntities.Add(entity, unitofWorkRepository);
};
}
public void RegisterRemoved(IAggregateRoot entity, IUnitOfWorkRepository unitofWorkRepository)
{
if (!deletedEntities.ContainsKey(entity))
{
deletedEntities.Add(entity, unitofWorkRepository);
}
}
public void Commit()
{
using (TransactionScope scope = new TransactionScope())
{
foreach (IAggregateRoot entity in this.addedEntities.Keys)
{
this.addedEntities[entity].PersistCreationOf(entity);
}
foreach (IAggregateRoot entity in this.changedEntities.Keys)
{
this.changedEntities[entity].PersistUpdateOf(entity);
}
foreach (IAggregateRoot entity in this.deletedEntities.Keys)
{
this.deletedEntities[entity].PersistDeletionOf(entity);
}
scope.Complete();
}
}
接下來,再添加一個AccountRepository類,這個類實現了兩個接口,IAccountRepository和IUnitOfWorkRepository接口。IAccountRepository中的方法就是簡單得將工作交給了Unit Of Work,傳入待持久化的實體及Repository(實現了IUnitOfWorkRepository)引用。最后Unit Of Work 引用Repository的IUnitOfWorkRepository契約來完成真正的持久化工作。
public class AccountRepository : IAccountRepository,IUnitOfWorkRepository
{
private IUnitOfWork _unitOfWork;
public AccountRepository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public void Save(Account account)
{
_unitOfWork.RegisterUpdate(account, this);
}
public void Add(Account account)
{
_unitOfWork.RegisterAdd(account, this);
}
public void Remove(Account account)
{
_unitOfWork.RegisterRemoved(account, this);
}
public void PersistUpdateOf(IAggregateRoot entity)
{
// ADO.net code to update the entity...真正的SQL實現
}
public void PersistCreationOf(IAggregateRoot entity)
{
// ADO.net code to Add the entity...真正的SQL實現
}
public void PersistDeletionOf(IAggregateRoot entity)
{
// ADO.net code to delete the entity...真正的SQL實現
}
}
總結下,UnitOfWork.Repository這里才是真正糾結的地方。首先,Unit Of Work加載實體對象(Accont)和實體對應的Repository對象(AccountRepository);然后通過Unit Of Work的Commit方法,循環轉調Repository對象(AccountRepository)的持久化方法,進行實體對象(Accont)的持久化工作。多調試就明白了。
UnitOfWork.Console
接下來,就是最后的控制台程序了。
class Program
{
static void Main(string[] args)
{
Account a = new Account(1000);
System.Console.WriteLine("現在張三,存有{0}", a.Balance);
Account b = new Account(200);
System.Console.WriteLine("現在李四,存有{0}", b.Balance);
System.Console.WriteLine("張三准備轉500元給李四,轉戰開始了......");
//聲明要使用的UnitOfWork
IUnitOfWork nhUnitOfWork = new NHUnitOfWork();
//聲明要使用的Repository
IAccountRepository accountRepository = new AccountRepository(nhUnitOfWork);
AccountService service = new AccountService(accountRepository, nhUnitOfWork);
service.Transfer(a,b,500);
System.Console.WriteLine("轉賬結束");
System.Console.WriteLine("張三當前余額:{0}",a.Balance);
System.Console.WriteLine("李四當前余額:{0}",b.Balance);
System.Console.ReadKey();
}
}