甚么是 Repository ?
这一天小明问我甚么是Repository ?
很多应用程式都有资料存取的需求,大多数使用的都是关联式资料库 (RDBMS),
大多数的开发人员使用ODBC, DB-Library, ADO.NET, OLE DB 等不同的介面,
再搭配 SQL 指令来存取资料.
为了要整合这么多种资料储存类型,最好的方法就是将要储存的物件抽取出来变成一个实体物件(Entity),
有了Entity 物件后,就可以试着传递Entity 资料到某个介面interface,以达到储存或读取Entity 的能力,
这个抽象化介面就是 Repository .
只要向资料储存体Repository 存放资料或要读取资料,如果资料储存体换了,
只要抽换掉 Repository interface 的实作即可,
上层的服务完全不需要异动, 而这也是 Repository 最终的目的。
Repository 有很多种实作方法,有很多人用一个Repository 代表一个DB 里面的 Table.
为了减少重複的程式码, 也有人是以Generic 的方式,写一种通用型的Repository , 如下示範通用型的IRepository
public interface IRepository<T>{ void Create(T entity); T Read(Expression<Func<T, bool>> predicate); void Update(T entity); void Delete(T entity); void SaveChanges();}
如果我同时要用2 个Table 以上怎么办?有人就写了IUnitOfWork 来做这件事情.
以下是 IUnitOfWork 程式码,
public interface IUnitOfWork{ IGenericRepository<Customer> Customer { get; } IGenericRepository<Order> Order { get; } Task<int> SaveChangeAsync();}
熟悉Entity Framework 的人, 可以发现这跟Entity Framework 架构一样,
上述IUnitOfWork 程式码可以呼应对应到下面的Entity Framework 程式码
public MyDbContext : DbContext{ public DbSet<Customer> Customer { get; set; } public DbSet<Order> Order { get; set; }}
在ORM / Entity Framework , 还没那么成熟的年代,
使用 Repository 模式的理由很充分,好处也明显.
当 Entity Framework 和 NHibernate 已经提供了同样, 甚至更多的功能, 我们还需要再多加一层 Repository 吗?
网路上可以找到很多文章,介绍如何搭配 Entity Framework 来设计我们的 Repository.
所以「怎么做」并不是问题,问题在于我们是否该这么做?
假如你原本用Entity Framework 去存取SQL Server,
有一天你想改为Sqlite, 你也只需提供DB Provider , 外层的程式码也不需要改动.
也能存取各种不同的资料库.
拿掉Repository 之后,单元测试还是可以做,只要在资料库存取方法提升到interface method,
仍然可以进行隔离测试.
其次,可抽换底层的资料存取元件当然很理想. 然而就算我们用Repository 把它包起来,难保不够周密.
除了担心开发时间成本效益,我也怀疑真实世界中有多少案子真的需要而且实际有「任意切换不同的储存媒体」的需求.
至于Unit of Work,其实 EF 的 ObjectContext 和 DbContext 已经提供了类似的功能.
如果在ORM 之上再叠一层 Repository,会让我们费好大功夫写一堆看起来长得很像的程式码,
增加程式的複杂性,却得不到相对比例的好处.
就我的观点而言,在不需要切换任意 实际储存资料体 的情况下,比较小而单纯的应用程式,
我不会使用Repository . 如果是比较大型或複杂的系统....嗯,
目前我是没遇过同一套系统还需要切换不同种类的资料储存媒体.