• DDD领域驱动设计初探(三):仓储Repository(下)


    前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势。本章还是继续来完善下仓储的设计。上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑。那么涉及到具体的实现的时候我们应该怎么做呢,本章就来说说仓储里面具体细节方便的知识。

    DDD领域驱动设计初探系列文章:

    一、对仓储接口以及实现基类的完善

    1、仓储实现基类的所有方法加上virtual关键字,方便具体的仓储在特定需求的时候override基类的方法。

    复制代码
      //仓储的泛型实现类
        public class EFBaseRepository<TEntity> : IRepository<TEntity> where TEntity : AggregateRoot
        {
            [Import(typeof(IEFUnitOfWork))]
            public IEFUnitOfWork UnitOfWork { get; set; }
    
            public EFBaseRepository()
            {
                Regisgter.regisgter().ComposeParts(this);
            }
    
            public virtual IQueryable<TEntity> Entities
            {
                get { return UnitOfWork.context.Set<TEntity>(); }
            }
    
            public virtual TEntity GetByKey(object key)
            {
                return UnitOfWork.context.Set<TEntity>().Find(key);
            }
    
            public virtual IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express)
            {
                Func<TEntity, bool> lamada = express.Compile();
                return UnitOfWork.context.Set<TEntity>().Where(lamada).AsQueryable<TEntity>();
            }
    
            public virtual int Insert(TEntity entity)
            {
                UnitOfWork.RegisterNew(entity);
                return UnitOfWork.Commit();
            }
    
            public virtual int Insert(IEnumerable<TEntity> entities)
            {
                foreach (var obj in entities)
                {
                    UnitOfWork.RegisterNew(obj);
                }
                return UnitOfWork.Commit();
            }
    
            public virtual int Delete(object id)
            {
                var obj = UnitOfWork.context.Set<TEntity>().Find(id);
                if (obj == null)
                {
                    return 0;
                }
                UnitOfWork.RegisterDeleted(obj);
                return UnitOfWork.Commit();
            }
    
            public virtual int Delete(TEntity entity)
            {
                UnitOfWork.RegisterDeleted(entity);
                return UnitOfWork.Commit();
            }
    
            public virtual int Delete(IEnumerable<TEntity> entities)
            {
                foreach (var entity in entities)
                {
                    UnitOfWork.RegisterDeleted(entity);
                }
                return UnitOfWork.Commit();
            }
    
            public virtual int Delete(Expression<Func<TEntity, bool>> express)
            {
                Func<TEntity, bool> lamada = express.Compile();
                var lstEntity = UnitOfWork.context.Set<TEntity>().Where(lamada);
                foreach (var entity in lstEntity)
                {
                    UnitOfWork.RegisterDeleted(entity);
                }
                return UnitOfWork.Commit();
            }
    
            public virtual int Update(TEntity entity)
            {
                UnitOfWork.RegisterModified(entity);
                return UnitOfWork.Commit();
            }
        }
    复制代码

    2、查询和删除增加了传参lamada表达式的方法

    仓储接口:

    复制代码
       public interface IRepository<TEntity> where TEntity : AggregateRoot
        {
            //...........
    
            #region 公共方法
    
            /// <summary>
            /// 根据lamada表达式查询集合
            /// </summary>
            /// <param name="selector">lamada表达式</param>
            /// <returns></returns>
            IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express);
    
                /// <summary>
            ///     根据lamada表达式删除对象
            /// </summary>
            /// <param name="selector"> lamada表达式 </param>
            /// <returns> 操作影响的行数 </returns>
            int Delete(Expression<Func<TEntity, bool>> express);
    
             //..........
      }
    复制代码

    仓储的实现

    复制代码
       //仓储的泛型实现类
        public class EFBaseRepository<TEntity> : IRepository<TEntity> where TEntity : AggregateRoot
        {
            //.............
    
            public virtual IQueryable<TEntity> Find(Expression<Func<TEntity, bool>> express)
            {
                Func<TEntity, bool> lamada = express.Compile();
                return UnitOfWork.context.Set<TEntity>().Where(lamada).AsQueryable<TEntity>();
            }
    
            public virtual int Delete(Expression<Func<TEntity, bool>> express)
            {
                Func<TEntity, bool> lamada = express.Compile();
                var lstEntity = UnitOfWork.context.Set<TEntity>().Where(lamada);
                foreach (var entity in lstEntity)
                {
                    UnitOfWork.RegisterDeleted(entity);
                }
                return UnitOfWork.Commit();
            }
    
        //.............
        }
    复制代码

    增加这两个方法之后,对于单表的一般查询都可以直接通过lamada表示式的方法传入即可,并且返回值为IQueryable类型。

    3、对于涉及到多张表需要连表的查询机制,我们还是通过神奇的Linq来解决。例如我们有一个通过角色取角色对应的菜单的接口需求。

    在菜单的仓储接口里面:

    复制代码
       /// <summary>
        /// 菜单这个聚合根的仓储接口
        /// </summary>
        public interface IMenuRepository:IRepository<TB_MENU>
        {
            IQueryable<TB_MENU> GetMenusByRole(TB_ROLE oRole);
        }
    复制代码

    对应仓储实现:

    复制代码
      [Export(typeof(IMenuRepository))]
        public class MenuRepository:EFBaseRepository<TB_MENU>,IMenuRepository
        {
            public IQueryable<TB_MENU> GetMenusByRole(TB_ROLE oRole)
            {
                var queryrole = UnitOfWork.context.Set<TB_ROLE>().AsQueryable();
                var querymenu = UnitOfWork.context.Set<TB_MENU>().AsQueryable();
                var querymenurole = UnitOfWork.context.Set<TB_MENUROLE>().AsQueryable();
                var lstres = from menu in querymenu
                             from menurole in querymenurole
                             from role in queryrole
                             where menu.MENU_ID == menurole.MENU_ID &&
                                   menurole.ROLE_ID == role.ROLE_ID &&
                                   role.ROLE_ID == oRole.ROLE_ID
                             select menu;
                return lstres;
            }
        }
    复制代码

     这里也是返回的IQueryable接口的集合,千万不要小看IQueryable接口,它是一种表达式树,可以延迟查询。也就是说,在我们执行GetMenusByRole()之后,得到的是一个带有查询sql语句的表达式树结构,并没有去数据库执行查询,只有在我们ToList()的时候才会去查询数据库。我们来写个Demo测试下。

    复制代码
       class Program
        {
            [Import]
            public IUserRepository userRepository { get; set; }
    
            [Import]
            public IMenuRepository menuRepository { get; set; }
    
            static void Main(string[] args)
            {
                //注册MEF
                var oProgram = new Program();
                Regisgter.regisgter().ComposeParts(oProgram);
    var lstFindUsers = oProgram.userRepository.Find(x => x.USER_NAME !=null);
                var lstRes = lstFindUsers.ToList();
                var lstMenu = oProgram.menuRepository.GetMenusByRole(new TB_ROLE() { ROLE_ID = "aaaa" });
                var lstMenuRes = lstMenu.ToList();
            }
        }
    复制代码

    来看执行过程:

     当程序执行var lstMenu = oProgram.menuRepository.GetMenusByRole(new TB_ROLE() { ROLE_ID = "aaaa" })这一步的时候基本是不耗时的,因为这一步仅仅是在构造表达式树,只有在.ToList()的时候才会有查询等待。更多详细可以看看此文 Repository 返回 IQueryable?还是 IEnumerable?

    在dax.net的系列文章中,提到了规约模式的概念,用于解决条件查询的问题。博主感觉这个东西设计确实牛叉,但实用性不太强,一般中小型的项目也用不上。有兴趣可以看看规约(Specification)模式

  • 相关阅读:
    [LeetCode] 75. 颜色分类(荷兰国旗)
    [LeetCode] 347. 前K个高频元素
    CMU-14445 数据库原理 汇总
    MIT-6.824 操作系统 汇总
    发布一个基于协程和事件循环的c++网络库
    记录一次gdb debug经历
    彻底弄懂UTF-8、Unicode、宽字符、locale
    CPU使用率原理及计算方式
    TCP使用注意事项总结
    STL-vector
  • 原文地址:https://www.cnblogs.com/Justsoso-WYH/p/10836504.html
Copyright © 2020-2023  润新知