这样设计是否更好些~仓储接口是否应该设计成基础操作接口和扩展操作接口
前言
我们进行linq to sql和ef时代后,底层的实现基本使用的是repository模块,即仓储模式,事实上就是把ORM实体的最基本操作进行封闭,对外层不公开操作实现的细节。
面向接口的编程
一个规定,多个实现,这可能是接口给我们带来的最直观的印象了,比如一个仓储在定义后,你可以用linq to sql实现它,也可以用ef去实现它,再或者使用ado.net去实现它,但它对外暴露的永远是稳定的接口,这里我们称为IRepository。
仓储接口是否应该设计成基础操作接口和扩展操作接口
这是今天说的重点,我们把仓储的最基本操作提炼出来,放到IRepository接口里,它叫做基本操作接口;将集合操作及其它附加操作放在IExtensionRepository接口里,我们称它为扩展操作接口;而我们再建立一个完成功能的接口ICompleteRepository,它会集成前两个接口,当调用方要求使用IRepository和IExtensionRepository的功能时,我们必须使用ICompleteRepository去声明对象,而简单的操作我们只需要使用IRepository去声明对象即可。
IRepository接口我们可以这样定义:
public interface IRepository<TEntity> where TEntity : class { /// <summary> /// 添加实体并提交到数据服务器 /// </summary> /// <param name="item">Item to add to repository</param> void Insert(TEntity item); /// <summary> /// 移除实体并提交到数据服务器 /// 如果表存在约束,需要先删除子表信息 /// </summary> /// <param name="item">Item to delete</param> void Delete(TEntity item); /// <summary> /// 修改实体并提交到数据服务器 /// </summary> /// <param name="item"></param> void Update(TEntity item); /// <summary> /// Get all elements of type {T} in repository /// </summary> /// <returns>List of selected elements</returns> IQueryable<TEntity> GetModel(); /// <summary> /// 根据主键得到实体 /// </summary> /// <param name="id"></param> /// <returns></returns> TEntity Find(params object[] id); }
对于扩展的功能接口,我们可以这样去定义:
/// <summary> /// 扩展的Repository操作规范 /// </summary> public interface IExtensionRepository<TEntity> where TEntity : class { /// <summary> /// 添加集合 /// </summary> /// <param name="item"></param> void Insert(IEnumerable<TEntity> item); /// <summary> /// 修改集合 /// </summary> /// <param name="item"></param> void Update(IEnumerable<TEntity> item); /// <summary> /// 删除集合 /// </summary> /// <param name="item"></param> void Delete(IEnumerable<TEntity> item); /// <summary> /// 扩展更新方法,只对EF支持 /// </summary> /// <param name="entity"></param> void Update(System.Linq.Expressions.Expression<Action<TEntity>> entity); /// <summary> /// 根据指定规约得到延时结果集 /// </summary> /// <param name="specification"></param> /// <returns></returns> IQueryable<TEntity> GetModel(Commons.Entity.Specification.ISpecification<TEntity> specification); /// <summary> /// 根据指定lambda得到延时结果集 /// </summary> /// <param name="predicate"></param> /// <returns></returns> IQueryable<TEntity> GetModel(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate); /// <summary> /// 根据指定规约得到实体 /// </summary> /// <param name="specification"></param> /// <returns></returns> TEntity Find(Commons.Entity.Specification.ISpecification<TEntity> specification); /// <summary> /// 根据指定lambda得到实体 /// </summary> /// <param name="predicate"></param> /// <returns></returns> TEntity Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate); }
而为了使业务层即有IRepository而且还有IExtensionRepository接口的功能,我们将建立一个完整操作的接口,它由仓储实现基类去集成,在业务层,可以声明
这个完整的仓储接口:
/// <summary> /// 完整的数据操作接口 /// 完整的接口中,包括两个事件 /// </summary> public interface ICompleteRepository<T> : IRepository<T>, IExtensionRepository<T> where T : class { /// <summary> /// Occurs after data saved /// </summary> event EventHandler<SavedEventArgs> AfterSaved; /// <summary> /// Occurs before data saved /// </summary> event EventHandler<SavedEventArgs> BeforeSaved; }
一个仓储基类将去实现它,当然你的仓储基类根据ORM不同,可以会有多个版本,你可以配合IOC来动态干这件事,呵呵!
如图:
再看一下传统DAL层图:(比较喜欢DDD架构,对于简单业务还是可以使用这种传统架构的)
好了,今天的仓储接口就讨论到这里吧,呵呵,对于一个问题的答案,在不同的场合,环境下很可能是不同的,而我们的架构也应该根据不同的业务需求,灵活去应对,这样,才会设计出更合理的架构!