文章出處

鋪墊

通常在使用 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 的次數。

這時,有幾種辦法:

  1. 應用程序的 Business Logic 中自己記錄,比如調用 Update() 操作后記錄。
  2. 使用 AOP 模式,在調用 CRUD 方法時注入計數器。
  3. 修改 Repository<T> 實現,在每個方法中嵌入計數器。
  4. 繼承 Repository<T> 類,在衍生類中嵌入計數器。
  5. 使用裝飾器模式封裝 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> 來完成這一個擴展。

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()