背景
考虑到目前中小企业应用的主流是ORM,我准备在NHibernate和EntityFramework之间找到一个抽象层,也就是说我准备只支持NHibernate和EntityFramework。
思路
NH和EF都实现了“工作单元”和“主键映射”这两种企业应用模式,而这两种模式其实就是管理一种状态机,如下图:
实现
工作单元接口
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Happy.Domain 8 { 9 ///10 /// 工作单元接口。 11 /// 12 ///13 /// 聚合状态: 14 /// 34 public interface IUnitOfWork : IDisposable 35 { 36 ///15 ///
20 /// 合法转换: 21 ///- transient:实例存在于内存中,但不存在于工作单元和数据库中。
16 ///- persistent in database:实例存在于数据库中。
17 ///- persistent in unitofwork:实例存在于工作单元中。
18 ///- detached:实例存在于内存和数据库中,但不存在于工作单元中。
19 ///22 ///
33 ///- transient > Save -> persistent in unitofwork,Flush时会生成Insert Sql,场景:从UI层创建实例,执行创建。
23 ///- detached -> Update -> persistent in unitofwork,Flush时会生成Update Sql,场景:从UI层重建实例,执行修改(支持离线乐观并发)。
24 ///- detached -> Persist -> persistent in unitofwork,Flush时不会生成Sql,场景:将实例从另一个工作单元脱钩,添加到当前工作单元。
25 ///- detached -> Delete -> persistent in unitofwork,Flush时会生成Delete Sql,场景:从UI层重建实例,删除记录。
26 ///- detached -> Merge -> persistent in unitofwork,Flush时会生成Update Sql,场景:从UI层重建实例,合并到从数据库重建的实例,执行修改(不支持离线乐观并发)。
27 ///- persistent in unitofwork -> Evict -> detached,Flush时不会生成Sql,场景:将实例从当前工作单元脱钩,添加到另一个工作单元。
28 ///- persistent in unitofwork -> Delete -> persistent in unitofwork,Flush时会生成Delete Sql,场景:从数据库重建实例,删除记录。
29 ///- persistent in unitofwork -> Flush -> persistent in database,提交工作单元,会生成SQL,场景:执行完一系列Create、Update和Delete后统一提交,只产生一次数据库往返。
30 ///- persistent in database -> Load -> persistent in unitofwork,从数据库重建实例。
31 ///- persistent in database -> Refresh -> persistent in unitofwork,从数据库刷新实例,场景:使用存储过程修改了一个实例,使用此方法重新刷新一下。
32 ///37 /// 判断 39 bool Contains是否 persistent in unitofwork。 38 /// (TAggregateRoot item) 40 where TAggregateRoot : AggregateRoot; 41 42 /// 43 /// transient > Save -> persistent in unitofwork,Flush时会生成Insert Sql,场景:从UI层创建实例,执行创建。 44 /// 45 void Save(TAggregateRoot item) 46 where TAggregateRoot : AggregateRoot; 47 48 /// 49 /// detached -> Update -> persistent in unitofwork,Flush时会生成Update Sql,场景:从UI层重建实例,执行修改(支持离线乐观并发)。 50 /// 51 void Update(TAggregateRoot item) 52 where TAggregateRoot : AggregateRoot; 53 54 /// 55 /// detached -> Persist -> persistent in unitofwork,Flush时不会生成Sql,场景:将实例从另一个工作单元脱钩,添加到当前工作单元。 56 /// 57 void Persist(TAggregateRoot item) 58 where TAggregateRoot : AggregateRoot; 59 60 /// 61 /// 执行如下两种转换: 62 /// 67 void Delete63 ///
66 ///- detached -> Delete -> persistent in unitofwork,Flush时会生成Delete Sql,场景:从UI层重建实例,删除记录。
64 ///- persistent in unitofwork -> Delete -> persistent in unitofwork,Flush时会生成Delete Sql,场景:从数据库重建实例,删除记录。
65 ///(TAggregateRoot item) 68 where TAggregateRoot : AggregateRoot; 69 70 /// 71 /// detached -> Merge -> persistent in unitofwork,Flush时会生成Update Sql,场景:从UI层重建实例,合并到从数据库重建的实例,执行修改(不支持离线乐观并发)。 72 /// 73 void Merge(TAggregateRoot item) 74 where TAggregateRoot : AggregateRoot; 75 76 /// 77 /// persistent in unitofwork -> Evict -> detached,Flush时不会生成Sql,场景:将实例从当前工作单元脱钩,添加到另一个工作单元。 78 /// 79 void Evict(TAggregateRoot item) 80 where TAggregateRoot : AggregateRoot; 81 82 /// 83 /// persistent in unitofwork -> Flush -> persistent in database,提交工作单元,会生成SQL,场景:执行完一系列Create、Update和Delete后统一提交,只产生一次数据库往返。 84 /// 85 void Flush(); 86 87 ///88 /// persistent in database -> Load -> persistent in unitofwork,从数据库重建实例。 89 /// 90 TAggregateRoot Load(Guid id) 91 where TAggregateRoot : AggregateRoot; 92 93 /// 94 /// persistent in database -> Refresh -> persistent in unitofwork,从数据库刷新实例,场景:使用存储过程修改了一个实例,使用此方法重新刷新一下。 95 /// 96 void Refresh(TAggregateRoot item) 97 where TAggregateRoot : AggregateRoot; 98 99 /// 100 /// 回滚所有自上次提交以后的修改。101 /// 102 void Clear();103 104 ///105 /// 清空处于persistent in unitofwork状态的实例。106 /// 107 TRepository GetRepository()108 where TRepository : IRepository;109 }110 }
基于EntityFramework的工作单元
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Data; 7 using System.Data.Entity; 8 using System.Data.Entity.Infrastructure; 9 10 using Microsoft.Practices.ServiceLocation; 11 12 using Happy.Domain; 13 using Happy.DesignByContract; 14 15 namespace Happy.EntityFramework 16 { 17 ///18 /// 基于EntityFramework的工作单元。 19 /// 20 public abstract class UnitOfWork : DbContext, IUnitOfWork 21 { 22 private readonly Dictionaryrepositories = new Dictionary (); 23 24 /// 25 /// 构造方法。 26 /// 27 protected UnitOfWork() 28 { 29 } 30 31 ///32 /// 构造方法。 33 /// 34 protected UnitOfWork(string nameOrConnectionString) 35 : base(nameOrConnectionString) 36 { 37 } 38 39 ///40 public bool Contains (TAggregateRoot item) 41 where TAggregateRoot : AggregateRoot 42 { 43 item.MustNotNull("item"); 44 45 return this.Entry(item).State != EntityState.Detached; 46 } 47 48 /// 49 public void Save (TAggregateRoot item) 50 where TAggregateRoot : AggregateRoot 51 { 52 item.MustNotNull("item"); 53 54 this.Set ().Add(item); 55 } 56 57 /// 58 public void Update (TAggregateRoot item) 59 where TAggregateRoot : AggregateRoot 60 { 61 item.MustNotNull("item"); 62 63 this.Entry(item).State = EntityState.Modified; 64 } 65 66 /// 67 public void Persist (TAggregateRoot item) 68 where TAggregateRoot : AggregateRoot 69 { 70 item.MustNotNull("item"); 71 72 this.Entry(item).State = EntityState.Unchanged; 73 } 74 75 /// 76 public void Delete (TAggregateRoot item) 77 where TAggregateRoot : AggregateRoot 78 { 79 item.MustNotNull("item"); 80 81 this.Entry(item).State = EntityState.Deleted; 82 } 83 84 /// 85 public void Merge (TAggregateRoot item) 86 where TAggregateRoot : AggregateRoot 87 { 88 item.MustNotNull("item"); 89 90 var persistItem = this.Set ().Find(item.Id); 91 92 this.Entry(persistItem).CurrentValues.SetValues(item); 93 } 94 95 /// 96 public void Evict (TAggregateRoot item) 97 where TAggregateRoot : AggregateRoot 98 { 99 item.MustNotNull("item");100 101 this.Entry(item).State = EntityState.Detached;102 }103 104 /// 105 public void Flush()106 {107 try108 {109 base.SaveChanges();110 }111 catch (DbUpdateConcurrencyException ex)112 {113 throw new OptimisticConcurrencyException(ex.Message, ex);114 }115 }116 117 public TAggregateRoot Load (Guid id)118 where TAggregateRoot : AggregateRoot119 {120 return this.Set ().Find(id);121 }122 123 public void Refresh (TAggregateRoot item)124 where TAggregateRoot : AggregateRoot125 {126 item.MustNotNull("item");127 128 this.Entry(item).Reload();129 }130 131 /// 132 public void Clear()133 {134 base.ChangeTracker.Entries()135 .ToList()136 .ForEach(entry => entry.State = System.Data.EntityState.Detached);137 }138 139 /// 140 public TRepository GetRepository ()141 where TRepository : IRepository142 {143 var key = typeof(TRepository);144 145 if (!repositories.ContainsKey(key))146 {147 var repository = ServiceLocator.Current.GetInstance ();148 (repository as IEntityFrameworkRepository).Owner = this;149 repositories[key] = repository;150 }151 152 return (TRepository)repositories[key];153 }154 }155 }
备注
其实我们经常忽略一个关于接口的问题,就是异常本身也是API的一部分,虽然这部分在C#中没有办法显式的表达,等我的朋友实现完了NH版本的工作单元的开发,我们就继续对异常进行抽象。