DDD领域驱动设计初探(三):仓储Repository(下)
前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势。本章还是继续来完善下仓储的设计。上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑。那么涉及到具体的实现的时候我们应该怎么做呢,本章就来说说仓储里面具体细节方便的知识。
DDD领域驱动设计初探系列文章:
- C#进阶系列——DDD领域驱动设计初探(一):聚合
- C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)
- C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)
- C#进阶系列——DDD领域驱动设计初探(四):WCF搭建
- C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用
- C#进阶系列——DDD领域驱动设计初探(六):领域服务
- C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建
一、对仓储接口以及实现基类的完善
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)模式。
DDD领域驱动设计初探(三):仓储Repository(下)的更多相关文章
- C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)
前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...
- C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)
前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...
- DDD领域驱动设计初探(二):仓储Repository(上)
前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...
- C#进阶系列——DDD领域驱动设计初探(一):聚合
前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...
- C#进阶系列——DDD领域驱动设计初探(四):WCF搭建
前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储.领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加 ...
- C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用
前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...
- C#进阶系列——DDD领域驱动设计初探(六):领域服务
前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈 ...
- C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建
前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码.之前的六篇完成了领域层.应用层.以及基础结构层的部分代码,这篇打算搭建下UI层的代码. DDD领域驱动设计初 ...
- DDD领域驱动设计初探
DDD领域驱动设计初探1 前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下D ...
随机推荐
- Bader分析
一.背景 理查德·贝德(Richard Bader)开发了一种将分子分解为原子的直观方法.他对原子的定义纯粹基于电子电荷密度.Bader使用所谓的零磁通表面来划分原子.零通量表面是2D表面,其上电荷密 ...
- udp组播的实现
组播在内核里面对应的一个重要的结构体是ip_mreq,如下: struct ip_mreq { struct in_addr imr_multiaddr; /* IP multicast addres ...
- leetcode 53. Maximum Subarray 、152. Maximum Product Subarray
53. Maximum Subarray 之前的值小于0就不加了.dp[i]表示以i结尾当前的最大和,所以需要用一个变量保存最大值. 动态规划的方法: class Solution { public: ...
- 【Python】—— 获取函数内部变量名称
原文出处: https://blog.csdn.net/maixiaochai/article/details/88693507 关键语句: func_vars = func.__code__.co_ ...
- 前端必须掌握的 docker 技能(3)
概述 作为一个前端,我觉得必须要学会使用 docker 干下面几件事: 部署前端应用 部署 nginx 给部署的 nginx 加上 https 使用 docker compose 进行部署 给 ngi ...
- 快速入门分布式消息队列之 RabbitMQ(3)
目录 目录 前文列表 前言 通道 Channel 一个基本的生产者消费者实现 消费者 生产者 运行结果 应用预取计数 应用 ACK 机制 最后 前文列表 快速入门分布式消息队列之 RabbitMQ(1 ...
- 九:flask-response响应
1.如果返回的是个合法的响应对象,则直接返回 同时,也可以在response里面做一些操作,比如增加cookie 2.如果返回的是一个字符串,那么flask会重新创建一个werkzeug,wrappe ...
- 测开之路一百四十三:ORM框架之SQLAlchemy模型及表创建
基于前一篇内容,可以使用模型的结构 目录结构 main,入口层 from flask import Flaskfrom flask_sqlalchemy import SQLAlchemy app = ...
- 常用获取Android崩溃日志和IOS崩溃日志的几种方法
一:前言 在日常测试app时,经常会遇到崩溃问题,测试快速抓取到崩溃日志可以有效方便开发进行定位,快速解决问题所在测试做到测试分析,定位是非常重要的,这也是判断一个测试能力指标的一大维度. 二:And ...
- linux常用终端指令+如何用vim写一个c程序并运行
在装好ubuntu之后今天学习了一些linux的一些基础知识: windows里面打开命令窗口是win+r,在linux系统里面,ctrl+alt+t打开终端,今天的一些指令都是围绕终端来说的 首先s ...