Repository模式完全实战引发的思考
前言:
这个月请假出去玩了几天,当了几天文艺青年,回来之后任务稍微紧了一点。放松之后回归正轨,好久没写了,也没什么好的东西值得分享,对于之前的文章好多朋友留言了,看来博客园的同学们对于技术还是比较踏实的。关于其中的一些问题有的我没有遇到过,我也不敢妄下断言,还是希望大家能够各种搜索自己解决吧。这篇简单说说Repository带来的思考吧。
什么是Repository模式:
其实在我们参考目前的C#框架或者代码的时候,已经发现这种模式早已渗透到了C#项目开发与架构之中,就如同当年的三层架构上的DAL。
Repository与Dal的区别:
Repository是DDD中的概念,强调Repository是受Domain驱动的,Repository中定义的功能要体现Domain的意图和约束。
Dal只是关心的DB。
如果不在项目中使用的话总感觉没跟上潮流,许多文章对其最简单的解释是:
一种用来封装存储,读取和查找行为的机制,它模拟了一个对象集合。
其实很多时候学习东西最快的方法就是:学以致用。知道什么之后,去用,在学,在重构,就OK了。
个人认为,Repository使用的如此广泛得益于.net方向的ORM的繁荣,就是因为开发者拜托了最基本的SQL操作,将其交由ORM处理,开发人员才有更多的精力去研究与折腾我们关心的业务和逻辑。
Repository模式主要是封装数据查询和存储逻辑。
当开发者减少了对SQL直接操作DB的依赖之后,他们就在考虑是否以同样的方法去摆脱其他数据存储介质的依赖,为了适配更多的数据媒介,就又提炼出了另一个Repository的特点,
Repository模式实际用途:更换、升级ORM引擎,不影响业务逻辑;
随着开发者能力的提高经验的提高,他们也面对着越来越多的业务分散的开发任务。比如做一个类似于Windows操作系统的软件,我们之前期望以一种简单线条去覆盖所有开发任务的经验变得不是那么的实际。随之业务的独特性与分散性,也许1-2个开发人员就需要去完成一个特定的模块与功能,开发过程适配其他API的任务就会变得琐碎。于是“单元测试”就变成了不可或缺的一部分,也就有了目前的“测试驱动”的开发策略,为实现能在最小的粒度,边界去测试功能。就有了Repository的另一个特性,
Repository模式能提高测试效率,单元测试时,用Mock对象代替实际的数据库存取,可以成倍地提高测试用例运行速度。
综上所述,任何一个模式的诞生,都是体现了开发者的智慧,但是其核心仍是我们看了许久,但又感觉索然无味的面向对象的基本原则:
这是里子,面子怎么换是为了适应时代的变迁,我们期待改变,但又不能忘记最真的。
废话扯了,上代码:
IRepositoryBase:这是所有的基类
public interface IRepositoryBase { } public interface IRepositoryBase<TEntity> : IRepositoryBase where TEntity : class { TestEntities DBContext { get; set; } IQueryable<TEntity> GetQueryList(); IQueryable<TEntity> GetQueryList(Expression<Func<TEntity, bool>> predicate); IEnumerable<TEntity> GetList(); IEnumerable<TEntity> GetList(Expression<Func<TEntity, bool>> predicate); int GetCount(Expression<Func<TEntity, bool>> predicate); TEntity Get(Expression<Func<TEntity, bool>> predicate); void Add(TEntity entity); void Update(TEntity entity); void Delete(TEntity entity); void Save(); }
ILogFirewallRepository:一个为了适配自身业务的Repository
public interface ILogFirewallRepository : IRepositoryBase<LogFirewall> { void UpdateLogEPTables(int EPID, string preValue, string nowValue, string user); }
RepositoryBase:对于基类通用任务的实现
public abstract class RepositoryBase { protected TestEntities _dbContext; public TestEntities DBContext { get { return _dbContext; } set { _dbContext = value; } } /// <summary> /// /// </summary> public RepositoryBase() { _dbContext = new TestEntities(); } /// <summary> /// /// </summary> public virtual void Save() { _dbContext.SaveChanges(); } /// <summary> /// /// </summary> /// <typeparam name="TEntity"></typeparam> /// <param name="sqlstr"></param> /// <param name="orderby">排序列</param> /// <param name="pageIndex">从1开始</param> /// <param name="pageCount"></param> /// <returns></returns> public IEnumerable<TEntity> GetPaging<TEntity>(string sqlstr, string orderby, int pageIndex, int pageCount, params object[] parameters) where TEntity : class { int start = 1; int end = PageConfig.PageSize; if (pageIndex > 0 && pageCount > 0) { start = pageCount * (pageIndex - 1) + 1; end = pageCount * ((pageIndex - 1) + 1); } Regex order = new Regex("-(asc|desc)$", RegexOptions.IgnoreCase); if (order.IsMatch(orderby)) { orderby = order.Replace(orderby, " $1"); } string sql = string.Format(@" SELECT * FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY {1} ) AS RowNumber, * FROM ({0} ) A_ ) AS A1_ WHERE RowNumber BETWEEN {2} AND {3} ORDER BY {1} ;" , sqlstr, orderby, start, end ); return _dbContext.Set<TEntity>().SqlQuery(sql, parameters); } } public class SyncRepositoryBase<TEntity> : RepositoryBase, IRepositoryBase<TEntity> where TEntity : class { IDbSet<TEntity> _dbSet; public SyncRepositoryBase() { this._dbSet = _dbContext.Set<TEntity>(); } public SyncRepositoryBase(TestEntities context) { this._dbContext = context; this._dbSet = this._dbContext.Set<TEntity>(); } public virtual IEnumerable<TEntity> GetList() { return _dbSet.ToList(); } public virtual IQueryable<TEntity> GetQueryList() { return _dbSet; } public virtual IQueryable<TEntity> GetQueryList(Expression<Func<TEntity, bool>> predicate) { return _dbSet.Where(predicate); } public virtual IEnumerable<TEntity> GetList(Expression<Func<TEntity, bool>> predicate) { return _dbSet.Where(predicate).ToList(); } public virtual int GetCount(Expression<Func<TEntity, bool>> predicate) { return _dbSet.Where(predicate).Count(); } public virtual TEntity Get(System.Linq.Expressions.Expression<Func<TEntity, bool>> predicate) { return _dbSet.Where(predicate).FirstOrDefault(); } public virtual void Add(TEntity entity) { _dbSet.Add(entity); } public virtual void Update(TEntity entity) { _dbContext.Entry(entity).State = EntityState.Modified; } public virtual void Delete(TEntity entity) { _dbSet.Remove(entity); } ///// <summary> ///// ///// </summary> //public virtual void Save() //{ // _dbContext.SaveChanges(); //} }
LogFirewallRepository:对自身业务的实现
public class LogFirewallRepository : SyncRepositoryBase<LogFirewall>, ILogFirewallRepository { public LogFirewallRepository() { } public LogFirewallRepository(TestEntities context) : base(context) { } public void UpdateLogEPTables(int EPID, string preValue, string nowValue, string user) { //update the logFireWall table var log = this.DBContext.LogFirewall as IQueryable<LogFirewall>; LogFirewall logEP = new LogFirewall() { Previous = preValue, After = nowValue, Handler = user, HandledDate = DateTime.Now, FirewallID = EPID }; this.Add(logEP); this.Save(); } }
在Repository之上习惯加入Service进行业务聚合(和Domain没关系)
IMQMessageService:提炼自身聚合任务
public interface IMQMessageService { void InsertANewMessage(Message message); }
ServicesBase<TEntity>:一些通用聚合任务的实现
public interface ServicesBase<TEntity> { IQueryable<TEntity> GetQueryList(); IQueryable<TEntity> GetQueryList(Expression<Func<TEntity, bool>> predicate); IEnumerable<TEntity> GetList(); IEnumerable<TEntity> GetList(Expression<Func<TEntity, bool>> predicate); int GetCount(Expression<Func<TEntity, bool>> predicate); TEntity Get(Expression<Func<TEntity, bool>> predicate); void Add(TEntity entity); void Update(TEntity entity); void Delete(TEntity entity); void Save(); }
MQMessageService:自身聚合任务实现
public class MQMessageService : ServicesBase<Message>, IMQMessageService { IMQMessageRepository msR = new MQMessageRepository(); public void InsertANewMessage(Message message) { msR.Add(message); msR.Save(); } }
以上只是最简单的Repository的使用,在不同业务需求与开发重点侧重不同的情况下,实现方式与封装使用也会不同。因为我这里的业务应该是后面的线程协调与资源并发处理,所以这里用到最简单就可以了。