准备抽象NHibernate和EntityFramework

准备抽象NHibernate和EntityFramework

背景

考虑到目前中小企业应用的主流是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     /// <summary>
 10     /// 工作单元接口。
 11     /// </summary>
 12     /// <remarks>
 13     /// 聚合状态:
 14     /// <list type="number">
 15     ///     <item>transient:实例存在于内存中,但不存在于工作单元和数据库中。</item>
 16     ///     <item>persistent in database:实例存在于数据库中。</item>
 17     ///     <item>persistent in unitofwork:实例存在于工作单元中。</item>
 18     ///     <item>detached:实例存在于内存和数据库中,但不存在于工作单元中。</item>
 19     /// </list>
 20     /// 合法转换:
 21     /// <list type="number">
 22     ///     <item>transient > Save -> persistent in unitofwork,Flush时会生成Insert Sql,场景:从UI层创建实例,执行创建。</item>
 23     ///     <item>detached -> Update -> persistent in unitofwork,Flush时会生成Update Sql,场景:从UI层重建实例,执行修改(支持离线乐观并发)。</item>
 24     ///     <item>detached -> Persist -> persistent in unitofwork,Flush时不会生成Sql,场景:将实例从另一个工作单元脱钩,添加到当前工作单元。</item>
 25     ///     <item>detached -> Delete -> persistent in unitofwork,Flush时会生成Delete Sql,场景:从UI层重建实例,删除记录。</item>
 26     ///     <item>detached -> Merge -> persistent in unitofwork,Flush时会生成Update Sql,场景:从UI层重建实例,合并到从数据库重建的实例,执行修改(不支持离线乐观并发)。</item>
 27     ///     <item>persistent in unitofwork -> Evict -> detached,Flush时不会生成Sql,场景:将实例从当前工作单元脱钩,添加到另一个工作单元。</item>
 28     ///     <item>persistent in unitofwork -> Delete -> persistent in unitofwork,Flush时会生成Delete Sql,场景:从数据库重建实例,删除记录。</item>
 29     ///     <item>persistent in unitofwork -> Flush -> persistent in database,提交工作单元,会生成SQL,场景:执行完一系列Create、Update和Delete后统一提交,只产生一次数据库往返。</item>
 30     ///     <item>persistent in database -> Load -> persistent in unitofwork,从数据库重建实例。</item>
 31     ///     <item>persistent in database -> Refresh -> persistent in unitofwork,从数据库刷新实例,场景:使用存储过程修改了一个实例,使用此方法重新刷新一下。</item>
 32     /// </list>
 33     /// </remarks>
 34     public interface IUnitOfWork : IDisposable
 35     {
 36         /// <summary>
 37         /// 判断<paramref name="item"/>是否 persistent in unitofwork。
 38         /// </summary>
 39         bool Contains<TAggregateRoot>(TAggregateRoot item)
 40             where TAggregateRoot : AggregateRoot;
 41 
 42         /// <summary>
 43         /// transient > Save -> persistent in unitofwork,Flush时会生成Insert Sql,场景:从UI层创建实例,执行创建。
 44         /// </summary>
 45         void Save<TAggregateRoot>(TAggregateRoot item)
 46             where TAggregateRoot : AggregateRoot;
 47 
 48         /// <summary>
 49         /// detached -> Update -> persistent in unitofwork,Flush时会生成Update Sql,场景:从UI层重建实例,执行修改(支持离线乐观并发)。
 50         /// </summary>
 51         void Update<TAggregateRoot>(TAggregateRoot item)
 52             where TAggregateRoot : AggregateRoot;
 53 
 54         /// <summary>
 55         /// detached -> Persist -> persistent in unitofwork,Flush时不会生成Sql,场景:将实例从另一个工作单元脱钩,添加到当前工作单元。
 56         /// </summary>
 57         void Persist<TAggregateRoot>(TAggregateRoot item)
 58             where TAggregateRoot : AggregateRoot;
 59 
 60         /// <summary>
 61         /// 执行如下两种转换:
 62         /// <list type="number">
 63         ///     <item>detached -> Delete -> persistent in unitofwork,Flush时会生成Delete Sql,场景:从UI层重建实例,删除记录。</item>
 64         ///     <item>persistent in unitofwork -> Delete -> persistent in unitofwork,Flush时会生成Delete Sql,场景:从数据库重建实例,删除记录。</item>
 65         /// </list>
 66         /// </summary>
 67         void Delete<TAggregateRoot>(TAggregateRoot item)
 68             where TAggregateRoot : AggregateRoot;
 69 
 70         /// <summary>
 71         /// detached -> Merge -> persistent in unitofwork,Flush时会生成Update Sql,场景:从UI层重建实例,合并到从数据库重建的实例,执行修改(不支持离线乐观并发)。
 72         /// </summary>
 73         void Merge<TAggregateRoot>(TAggregateRoot item)
 74             where TAggregateRoot : AggregateRoot;
 75 
 76         /// <summary>
 77         /// persistent in unitofwork -> Evict -> detached,Flush时不会生成Sql,场景:将实例从当前工作单元脱钩,添加到另一个工作单元。
 78         /// </summary>
 79         void Evict<TAggregateRoot>(TAggregateRoot item)
 80             where TAggregateRoot : AggregateRoot;
 81 
 82         /// <summary>
 83         /// persistent in unitofwork -> Flush -> persistent in database,提交工作单元,会生成SQL,场景:执行完一系列Create、Update和Delete后统一提交,只产生一次数据库往返。
 84         /// </summary>
 85         void Flush();
 86 
 87         /// <summary>
 88         /// persistent in database -> Load -> persistent in unitofwork,从数据库重建实例。
 89         /// </summary>
 90         TAggregateRoot Load<TAggregateRoot>(Guid id)
 91             where TAggregateRoot : AggregateRoot;
 92 
 93         /// <summary>
 94         /// persistent in database -> Refresh -> persistent in unitofwork,从数据库刷新实例,场景:使用存储过程修改了一个实例,使用此方法重新刷新一下。
 95         /// </summary>
 96         void Refresh<TAggregateRoot>(TAggregateRoot item)
 97             where TAggregateRoot : AggregateRoot;
 98 
 99         /// <summary>
100         /// 回滚所有自上次提交以后的修改。
101         /// </summary>
102         void Clear();
103 
104         /// <summary>
105         /// 清空处于persistent in unitofwork状态的实例。
106         /// </summary>
107         TRepository GetRepository<TRepository>()
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     /// <summary>
 18     /// 基于EntityFramework的工作单元。
 19     /// </summary>
 20     public abstract class UnitOfWork : DbContext, IUnitOfWork
 21     {
 22         private readonly Dictionary<Type, IRepository> repositories = new Dictionary<Type, IRepository>();
 23 
 24         /// <summary>
 25         /// 构造方法。
 26         /// </summary>
 27         protected UnitOfWork()
 28         {
 29         }
 30 
 31         /// <summary>
 32         /// 构造方法。
 33         /// </summary>
 34         protected UnitOfWork(string nameOrConnectionString)
 35             : base(nameOrConnectionString)
 36         {
 37         }
 38 
 39         /// <inheritdoc />
 40         public bool Contains<TAggregateRoot>(TAggregateRoot item)
 41             where TAggregateRoot : AggregateRoot
 42         {
 43             item.MustNotNull("item");
 44 
 45             return this.Entry(item).State != EntityState.Detached;
 46         }
 47 
 48         /// <inheritdoc />
 49         public void Save<TAggregateRoot>(TAggregateRoot item)
 50             where TAggregateRoot : AggregateRoot
 51         {
 52             item.MustNotNull("item");
 53 
 54             this.Set<TAggregateRoot>().Add(item);
 55         }
 56 
 57         /// <inheritdoc />
 58         public void Update<TAggregateRoot>(TAggregateRoot item)
 59             where TAggregateRoot : AggregateRoot
 60         {
 61             item.MustNotNull("item");
 62 
 63             this.Entry(item).State = EntityState.Modified;
 64         }
 65 
 66         /// <inheritdoc />
 67         public void Persist<TAggregateRoot>(TAggregateRoot item)
 68             where TAggregateRoot : AggregateRoot
 69         {
 70             item.MustNotNull("item");
 71 
 72             this.Entry(item).State = EntityState.Unchanged;
 73         }
 74 
 75         /// <inheritdoc />
 76         public void Delete<TAggregateRoot>(TAggregateRoot item)
 77             where TAggregateRoot : AggregateRoot
 78         {
 79             item.MustNotNull("item");
 80 
 81             this.Entry(item).State = EntityState.Deleted;
 82         }
 83 
 84         /// <inheritdoc />
 85         public void Merge<TAggregateRoot>(TAggregateRoot item)
 86             where TAggregateRoot : AggregateRoot
 87         {
 88             item.MustNotNull("item");
 89 
 90             var persistItem = this.Set<TAggregateRoot>().Find(item.Id);
 91 
 92             this.Entry(persistItem).CurrentValues.SetValues(item);
 93         }
 94 
 95         /// <inheritdoc />
 96         public void Evict<TAggregateRoot>(TAggregateRoot item)
 97             where TAggregateRoot : AggregateRoot
 98         {
 99             item.MustNotNull("item");
100 
101             this.Entry(item).State = EntityState.Detached;
102         }
103 
104         /// <inheritdoc />
105         public void Flush()
106         {
107             try
108             {
109                 base.SaveChanges();
110             }
111             catch (DbUpdateConcurrencyException ex)
112             {
113                 throw new OptimisticConcurrencyException(ex.Message, ex);
114             }
115         }
116 
117         public TAggregateRoot Load<TAggregateRoot>(Guid id)
118             where TAggregateRoot : AggregateRoot
119         {
120             return this.Set<TAggregateRoot>().Find(id);
121         }
122 
123         public void Refresh<TAggregateRoot>(TAggregateRoot item)
124             where TAggregateRoot : AggregateRoot
125         {
126             item.MustNotNull("item");
127 
128             this.Entry(item).Reload();
129         }
130 
131         /// <inheritdoc />
132         public void Clear()
133         {
134             base.ChangeTracker.Entries()
135                               .ToList()
136                               .ForEach(entry => entry.State = System.Data.EntityState.Detached);
137         }
138 
139         /// <inheritdoc />
140         public TRepository GetRepository<TRepository>()
141             where TRepository : IRepository
142         {
143             var key = typeof(TRepository);
144 
145             if (!repositories.ContainsKey(key))
146             {
147                 var repository = ServiceLocator.Current.GetInstance<TRepository>();
148                 (repository as IEntityFrameworkRepository).Owner = this;
149                 repositories[key] = repository;
150             }
151 
152             return (TRepository)repositories[key];
153         }
154     }
155 }
复制代码

备注

其实我们经常忽略一个关于接口的问题,就是异常本身也是API的一部分,虽然这部分在C#中没有办法显式的表达,等我的朋友实现完了NH版本的工作单元的开发,我们就继续对异常进行抽象。

原文地址:https://www.cnblogs.com/Leo_wl/p/3109751.html