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 ...
随机推荐
- linux上的常用的系统自带命令
wikipedia 发现的分类,发现还有好多没用过.. Unix command-line interface programs and shell builtins File system ca ...
- 四十一、python面向对象二
A.成员: 1.字段:静态字段(每个对象都有同一字段),普通字典(每个对象都有不同的数据) 2.方法:静态方法(无需使用对象的内容),类方法,普通方法(使用对象中的数据) 3.特性:普通特性(将方法伪 ...
- windows的 附件到底是什么东东?
附件, 包括其父目录"所有程序" -> "开始菜单", 其实都是一个目录而已!! 要对"开始菜单"下的所有内容进行 自定义 : 添加删 ...
- 在JavaScript中引用类型和值类型的区别
一.存储方式不一样 基本数据类型 变量存储的是简单的数据段,存储的是具体的值,是轻量级的数据存储方式 引用类型 引用类型的值,可以由多个值构成的对象,引用类型的变量存储的是对象引用地址.引用类型是重量 ...
- Python 笔试集(2):你不知道的 Python 整数
面试题 分别给出下述代码在终端(e.g. IPyhon)中和在程序中的运行结果: a = 256 b = 256 c = 257 d = 257 def foo(): e = 257 f = 257 ...
- 【C语言工具】AddressSanitizer - 内存检测工具
Github 地址:https://github.com/google/sanitizers Wiki 地址:https://github.com/google/sanitizers/wiki/Add ...
- c++ 事件回调 java
#pragma once #ifdef __cplusplus extern "C" { #endif typedef void(*sig_t)(int); int FirstEl ...
- linux下搭建mysql数据库
linux下搭建mysql数据库 1.下载mysql: http://dev.mysql.com/downloads/mysql/5.6.html#downloads wget http://dev. ...
- 初学node.js-nodejs中实现用户注册路由
经过前面几次的学习,已经可以做下小功能,今天要实现的事用户注册路由. 一.users_model.js 功能:定义用户对象模型 var mongoose=require('mongoose'), S ...
- Linux window 安装tomcat各版本下载地址
1.地址 https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/