工作单元模式(UnitOfWork)学习总结
工作单元的目标是维护变化的对象列表。使用IUnitOfWorkRepository负责对象的持久化,使用IUnitOfWork收集变化的对象,并将变化的对象放到各自的增删改列表中,
最后Commit,Commit时需要循环遍历这些列表,并由Repository来持久化。
要实现一个银行卡简单转账的功能,Demo框架如下设计:
代码实现如下:
EntityBase,领域类的基类。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Jack.Gao.UnitOfWork.Infrastructure { public class EntityBase { } }
IUnitOfWork,复杂维护变化的对象列表,并最后Commit,依次遍历变化的列表,并持久化,这就是Commit的事情。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Jack.Gao.UnitOfWork.Infrastructure { public interface IUnitOfWork { void RegisterAdded(EntityBase entityBase, IUnitOfWorkRepository unitOfWorkRepository); void RegisterChangeded(EntityBase entityBase, IUnitOfWorkRepository unitOfWorkRepository); void RegisterRemoved(EntityBase entityBase, IUnitOfWorkRepository unitOfWorkRepository); void Commit(); } }
IUnitOfWorkRepository,负责持久化对象。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Jack.Gao.UnitOfWork.Infrastructure { public interface IUnitOfWorkRepository { void PersistNewItem(EntityBase entityBase); void PersistUpdatedItem(EntityBase entityBase); void PersistDeletedItem(EntityBase entityBase); } }
UnitOfWork,IUnitOfWork的具体实现。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Transactions; 7 8 namespace Jack.Gao.UnitOfWork.Infrastructure 9 { 10 public class UnitOfWork:IUnitOfWork 11 { 12 #region Fields 13 14 private Dictionary<EntityBase, IUnitOfWorkRepository> addedEntities; 15 private Dictionary<EntityBase, IUnitOfWorkRepository> changededEntities; 16 private Dictionary<EntityBase, IUnitOfWorkRepository> removedEntities; 17 18 #endregion 19 20 #region Constructor 21 22 public UnitOfWork() 23 { 24 addedEntities=new Dictionary<EntityBase, IUnitOfWorkRepository>(); 25 changededEntities=new Dictionary<EntityBase, IUnitOfWorkRepository>(); 26 removedEntities=new Dictionary<EntityBase, IUnitOfWorkRepository>(); 27 } 28 29 #endregion 30 31 #region Implement IUnitOfWork 32 33 public void RegisterAdded(EntityBase entityBase, IUnitOfWorkRepository unitOfWorkRepository) 34 { 35 this.addedEntities.Add(entityBase,unitOfWorkRepository); 36 } 37 38 public void RegisterChangeded(EntityBase entityBase, IUnitOfWorkRepository unitOfWorkRepository) 39 { 40 this.changededEntities.Add(entityBase,unitOfWorkRepository); 41 } 42 43 public void RegisterRemoved(EntityBase entityBase, IUnitOfWorkRepository unitOfWorkRepository) 44 { 45 this.removedEntities.Add(entityBase,unitOfWorkRepository); 46 } 47 48 public void Commit() 49 { 50 using (TransactionScope transactionScope=new TransactionScope()) 51 { 52 foreach (var entity in addedEntities.Keys) 53 { 54 addedEntities[entity].PersistNewItem(entity); 55 } 56 57 foreach (var entity in changededEntities.Keys) 58 { 59 changededEntities[entity].PersistUpdatedItem(entity); 60 } 61 62 foreach (var entity in removedEntities.Keys) 63 { 64 removedEntities[entity].PersistDeletedItem(entity); 65 } 66 67 transactionScope.Complete(); 68 } 69 } 70 71 #endregion 72 } 73 }
BankAccount,继承自领域基类EntityBase。
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Jack.Gao.UnitOfWork.Infrastructure; namespace Jack.gao.UnitOfWork.Domain { public class BankAccount:EntityBase { #region Field public int Id { get; set; } public decimal Balance { get; set; } #endregion #region operator + public static BankAccount operator+(BankAccount accountLeft,BankAccount accountRight) { BankAccount account = new BankAccount(); account.Balance = accountLeft.Balance + accountRight.Balance; return account; } #endregion } }
IAccountRepository,持久化BankAcount接口
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Jack.gao.UnitOfWork.Domain { public interface IAccountRepository { void Save(BankAccount account); void Add(BankAccount account); void Remove(BankAccount account); } }
BankAccountService,服务类,实现转账服务。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Jack.Gao.UnitOfWork.Infrastructure; 7 8 namespace Jack.gao.UnitOfWork.Domain 9 { 10 public class BankAccountService 11 { 12 #region Field 13 14 private IAccountRepository _accountRepository; 15 private IUnitOfWork _unitOfWork; 16 17 #endregion 18 19 #region Constructor 20 21 public BankAccountService(IAccountRepository accountRepository, IUnitOfWork unitOfWork) 22 { 23 this._accountRepository = accountRepository; 24 this._unitOfWork = unitOfWork; 25 } 26 27 #endregion 28 29 #region Method 30 31 public void TransferMoney(BankAccount from, BankAccount to, decimal balance) 32 { 33 if (from.Balance>=balance) 34 { 35 from.Balance = from.Balance - balance; 36 to.Balance = to.Balance + balance; 37 38 _accountRepository.Save(from); 39 _accountRepository.Save(to); 40 _unitOfWork.Commit(); 41 } 42 } 43 44 #endregion 45 } 46 }
AccountRepository,持久化具体实现,使用ADO.NET实现,也可以使用其他的EF,NHbernate
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Jack.gao.UnitOfWork.Domain; 7 using Jack.Gao.UnitOfWork.Infrastructure; 8 using System.Data.SqlClient; 9 10 namespace Jack.gao.UnitOfWork.Persistence 11 { 12 public class AccountRepository:IAccountRepository,IUnitOfWorkRepository 13 { 14 #region Field 15 16 private const string _connectionString = @"Data Source=T57649\MSSQLSERVER2012;Initial Catalog=DB_Customer;Integrated Security=True"; 17 18 private IUnitOfWork _unitOfWork; 19 20 #endregion 21 22 #region Constructor 23 24 public AccountRepository(IUnitOfWork unitOfWork) 25 { 26 this._unitOfWork = unitOfWork; 27 } 28 29 #endregion 30 31 #region Implement interface IAccountRepository,IUnitOfWorkRepository 32 33 public void Save(BankAccount account) 34 { 35 _unitOfWork.RegisterChangeded(account,this); 36 } 37 38 public void Add(BankAccount account) 39 { 40 _unitOfWork.RegisterAdded(account,this); 41 } 42 43 public void Remove(BankAccount account) 44 { 45 _unitOfWork.RegisterRemoved(account,this); 46 } 47 48 public void PersistNewItem(EntityBase entityBase) 49 { 50 BankAccount account = (BankAccount)entityBase; 51 52 string insertAccountSql = string.Format("insert into DT_Account(balance,Id) values({0},{1})", account.Balance, account.Id); 53 54 SqlConnection sqlConnection = new SqlConnection(_connectionString); 55 56 try 57 { 58 sqlConnection.Open(); 59 60 SqlCommand sqlCommand = new SqlCommand(insertAccountSql, sqlConnection); 61 62 sqlCommand.ExecuteNonQuery(); 63 } 64 catch (Exception ex) 65 { 66 throw ex; 67 } 68 finally 69 { 70 sqlConnection.Close(); 71 } 72 } 73 74 public void PersistUpdatedItem(EntityBase entityBase) 75 { 76 BankAccount account = (BankAccount)entityBase; 77 78 string updateAccountSql = string.Format("update DT_Account set balance={0} where Id={1}", account.Balance,account.Id); 79 80 SqlConnection sqlConnection = new SqlConnection(_connectionString); 81 82 try 83 { 84 sqlConnection.Open(); 85 86 SqlCommand sqlCommand = new SqlCommand(updateAccountSql, sqlConnection); 87 88 sqlCommand.ExecuteNonQuery(); 89 } 90 catch (Exception ex) 91 { 92 throw ex; 93 } 94 finally 95 { 96 sqlConnection.Close(); 97 } 98 } 99 100 public void PersistDeletedItem(EntityBase entityBase) 101 { 102 BankAccount account = (BankAccount)entityBase; 103 104 string deleteAccountSql = string.Format("delete from DT_Account where Id={0}", account.Id); 105 106 SqlConnection sqlConnection = new SqlConnection(_connectionString); 107 108 try 109 { 110 sqlConnection.Open(); 111 112 SqlCommand sqlCommand = new SqlCommand(deleteAccountSql, sqlConnection); 113 114 sqlCommand.ExecuteNonQuery(); 115 } 116 catch (Exception ex) 117 { 118 throw ex; 119 } 120 finally 121 { 122 sqlConnection.Close(); 123 } 124 } 125 126 #endregion 127 128 #region Method 129 130 public BankAccount GetAccount(BankAccount account) 131 { 132 account.Balance = 100; 133 return account; 134 } 135 136 #endregion 137 } 138 }
AccountRepositoryTest,测试AccountRepository中的方法
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Jack.gao.UnitOfWork.Domain; using Jack.Gao.UnitOfWork.Infrastructure; using Jack.gao.UnitOfWork.Persistence; namespace Jack.gao.UnitOfWork.Test { [TestClass] public class AccountRepositoryTest { private IUnitOfWork unitOfWork; private IAccountRepository accountRepository; private BankAccountService accountService; public AccountRepositoryTest() { unitOfWork = new Jack.Gao.UnitOfWork.Infrastructure.UnitOfWork(); accountRepository = new AccountRepository(unitOfWork); accountService = new BankAccountService(accountRepository, unitOfWork); } [TestMethod] public void Add() { var accountLeft = new BankAccount() { Balance = 200, Id = 1 }; var accountRight = new BankAccount() { Balance = 10, Id = 2 }; accountRepository.Add(accountLeft); accountRepository.Add(accountRight); unitOfWork.Commit(); } [TestMethod] public void Save() { var accountLeft = new BankAccount() { Balance = 200, Id = 1 }; var accountRight = new BankAccount() { Balance = 10, Id = 2 }; accountService.TransferMoney(accountLeft, accountRight, 100); } [TestMethod] public void Remove() { var accountLeft = new BankAccount() { Balance = 200, Id = 1 }; accountRepository.Remove(accountLeft); unitOfWork.Commit(); } } }