鋪墊
通常在使用 EntityFramework 時,我們會封裝出 IRepository 和 IUnitOfWork 接口,前者負責 CRUD 操作,后者負責數據提交 Commit。
1 public interface IRepository<T> 2 where T : class 3 { 4 IQueryable<T> Query(); 5 6 void Insert(T entity); 7 8 void Update(T entity, params Expression<Func<T, object>>[] modifiedPropertyLambdas); 9 10 void Delete(T entity); 11 }
1 public interface IUnitOfWork 2 { 3 void Commit(); 4 }
然后,通過使用 Unity IoC 容器來注冊泛型接口與實現類型。
1 Func<IUnityContainer> factory = () => 2 { 3 return new UnityContainer() 4 .RegisterType(typeof(IRepository<>), typeof(Repository<>), new ContainerControlledLifetimeManager()) 5 .RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager()) 6 .RegisterType<DbContext, MyDBContext>(new ContainerControlledLifetimeManager()) 7 .RegisterType<DbContextAdapter>(new ContainerControlledLifetimeManager()) 8 .RegisterType<IObjectSetFactory, DbContextAdapter>(new ContainerControlledLifetimeManager()) 9 .RegisterType<IObjectContext, DbContextAdapter>(new ContainerControlledLifetimeManager()); 10 };
進而使與數據庫相關的操作在 Bisuness Logic 中呈現的非常簡單。
例如,通過一系列封裝,我們可以達到如下效果:
1 Customer customer = new Customer() 2 { 3 ID = "123", 4 FirstName = "Dennis", 5 LastName = "Gao", 6 }; 7 Repository.Customer.Insert(customer); 8 Repository.Commit();
查詢操作也是一句話搞定:
1 Customer customer = Repository.Customer.Query().SingleOrDefault(c => c.ID == "123");
需求
假設有一個新的需求:要求在應用層面記錄對每個 Table 的 CRUD 的次數。
這時,有幾種辦法:
- 應用程序的 Business Logic 中自己記錄,比如調用 Update() 操作后記錄。
- 使用 AOP 模式,在調用 CRUD 方法時注入計數器。
- 修改 Repository<T> 實現,在每個方法中嵌入計數器。
- 繼承 Repository<T> 類,在衍生類中嵌入計數器。
- 使用裝飾器模式封裝 Repository<T>,在新的 RepositoryDecorator<T> 類中嵌入計數器。
考慮到前三種方法均需要改動已有代碼,主要是涉及的修改太多,所有沒有嘗試采用。
方法 4 則要求修改 Repository<T> 的實現,為 CRUD 方法添加 virtual 關鍵字以便擴展。
方法 5 不需要修改 Repository<T> 的實現,對已有代碼的改動不大。
綜上所述,我們選擇了方法 5。
Repository 裝飾器基類實現
為便于以后的擴展,創建一個裝飾器的抽象類。
1 public abstract class RepositoryDecorator<T> : IRepository<T> 2 where T : class 3 { 4 private readonly IRepository<T> _surrogate; 5 6 protected RepositoryDecorator(IRepository<T> surrogate) 7 { 8 _surrogate = surrogate; 9 } 10 11 protected IRepository<T> Surrogate 12 { 13 get { return _surrogate; } 14 } 15 16 #region IRepository<T> Members 17 18 public virtual IQueryable<T> Query() 19 { 20 return _surrogate.Query(); 21 } 22 23 public virtual void Insert(T entity) 24 { 25 _surrogate.Insert(entity); 26 } 27 28 public virtual void Update(T entity, params Expression<Func<T, object>>[] modifiedPropertyLambdas) 29 { 30 _surrogate.Update(entity, modifiedPropertyLambdas); 31 } 32 33 public virtual void Delete(T entity) 34 { 35 _surrogate.Delete(entity); 36 } 37 38 #endregion 39 }
可以看到,RepositoryDecorator<T> 類型仍然實現了 IRepository<T> 接口,對外使用沒有任何變化。
實現需求
我們定義一個 CountableRepository<T> 類用于封裝 CRUD 計數功能,其繼承自 RepositoryDecorator<T> 抽象類。
1 public class CountableRepository<T> : RepositoryDecorator<T> 2 where T : class 3 { 4 public CountableRepository(IRepository<T> surrogate) 5 : base(surrogate) 6 { 7 } 8 9 public override IQueryable<T> Query() 10 { 11 PerformanceCounter.CountQuery<T>(); 12 return base.Query(); 13 } 14 15 public override void Insert(T entity) 16 { 17 PerformanceCounter.CountInsert<T>(); 18 base.Insert(entity); 19 } 20 21 public override void Update(T entity, params Expression<Func<T, object>>[] modifiedPropertyLambdas) 22 { 23 PerformanceCounter.CountUpdate<T>(); 24 base.Update(entity, modifiedPropertyLambdas); 25 } 26 27 public override void Delete(T entity) 28 { 29 PerformanceCounter.CountDelete<T>(); 30 base.Delete(entity); 31 } 32 }
我們在 override 方法中,添加了 CRUD 的計數功能。這里的代碼簡寫為:
1 PerformanceCounter.CountQuery<T>();
對原有代碼的修改則是需要注冊新的 CountableRepository<T> 類型。
1 Func<IUnityContainer> factory = () => 2 { 3 return new UnityContainer() 4 .ReplaceBehaviorExtensionsWithSafeExtension() 5 .RegisterType(typeof(IRepository<>), typeof(Repository<>), new ContainerControlledLifetimeManager()) 6 .RegisterType(typeof(CountableRepository<>), new ContainerControlledLifetimeManager()) 7 .RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager()) 8 .RegisterType<DbContext, MyDBContext>(new ContainerControlledLifetimeManager()) 9 .RegisterType<DbContextAdapter>(new ContainerControlledLifetimeManager()) 10 .RegisterType<IObjectSetFactory, DbContextAdapter>(new ContainerControlledLifetimeManager()) 11 .RegisterType<IObjectContext, DbContextAdapter>(new ContainerControlledLifetimeManager()); 12 };
擴展應用
既然有了抽象基類 RepositoryDecorator<T> ,我們可以從其設計衍生多個特定場景的 Repository 。
比如,當我們需要為某個 Table 的 Entity 添加緩存功能時,我們可以定制一個 CachableRepository<T> 來完成這一個擴展。
文章列表