EF架构~引入规约(Specification)模式,让程序扩展性更强
规约(Specification)模式:第一次看到这东西是在microsoft NLayer项目中,它是微软对DDD的解说,就像petshop告诉了我们MVC如何使用一样,这个规约模式最重要的作用是实现了查询语句与查询条件的分离,查询语句在底层是稳定的,不变的,而查询条件是和具体业务,具体领域有关的,是易变的,如果我们为每一个领域的每一个新需求都写一个新的方法,那就会出现很多重复的代码,不利于程序的最终扩展!
下面我们来看一个经典例子
一个IOrderRepository的接口,定义了一个订单仓储
Order_Info GetOrder_InfoById(int orderID);
List<Order_Info> GetOrder_Info(DateTime from, DateTime to);
List<Order_Info> GetOrder_InfoByUser(int userID);
代码本身没有任何问题,你只要去实现它就可以了,当一个新的需求到了之后,你的接口要被扩展(这是不被提倡的,一般我们会新建一个接口),然后修改
原来的实现类,去实现接口新的方法(违背了OCP原则),这种做法是大多部开发团队所经历了,我,一个普通的人,也经历了,但当我知道DDD后,当我看完
microsoft Nlayer项目之后,我知道,我一定要改变这种局面,于是,代码在规约模式的指导下,进行重构了,呵呵。
先看一下规约模式的类关系图
下面是我对原来结构的修改(由于原程序是三层架构,所以我就不改变原有架构了,只是对代码进行重构,DAL层,BLL层,WEB层)
DAL层
IRepository仓储接口如下,怎么去实现就不放了,呵呵
public interface IRepository<TEntity>
where TEntity : class
{
/// <summary>
/// 添加实体并提交到数据服务器
/// </summary>
/// <param name="item">Item to add to repository</param>
void Add(TEntity item); /// <summary>
/// 移除实体并提交到数据服务器
/// 如果表存在约束,需要先删除子表信息
/// </summary>
/// <param name="item">Item to delete</param>
void Remove(TEntity item); /// <summary>
/// 修改实体并提交到数据服务器
/// </summary>
/// <param name="item"></param>
void Modify(TEntity item); /// <summary>
/// 通过指定规约,得到实体对象
/// </summary>
/// <param name="specification"></param>
/// <returns></returns>
TEntity GetEntity(ISpecification<TEntity> specification); /// <summary>
/// 通用表达式树,得到实体
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
TEntity GetEntity(Expression<Func<TEntity, bool>> predicate); /// <summary>
/// Get all elements of type {T} in repository
/// </summary>
/// <returns>List of selected elements</returns>
IQueryable<TEntity> GetEntities(); /// <summary>
/// Get all elements of type {T} that matching a
/// Specification <paramref name="specification"/>
/// </summary>
/// <param name="specification">Specification that result meet</param>
/// <returns></returns>
IQueryable<TEntity> GetEntities(ISpecification<TEntity> specification); /// <summary>
/// 通用表达式树,得到集合
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> predicate);
}
IOrderRepository接口如下
public interface IOrderRepository :
Domain.Core.IRepository<Order_Info>
{
void InsertOrder(Order_Info entity);
}
DAL底层为数据持久化层,它是非常稳定的,只提供最基本的表操作,具体业务如何组成,全放在BLL层去实现
BLL层
这一层中定义具体业务的规约,并组成查询方法及调用DAL层的具体方法(DAL层来接受从BLL层传过来的ISpecification参数)
/// <summary>
/// 根据下单日期得到订单列表
/// </summary>
public class OrderFromDateSpecification : Specification<Order_Info>
{
DateTime? _fromDate;
DateTime? _toDate;
public OrderFromDateSpecification(DateTime? fromDate, DateTime? toDate)
{
_fromDate = fromDate ?? DateTime.MinValue;
_toDate = toDate ?? DateTime.MaxValue;
}
public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy()
{
Specification<Order_Info> spec = new TrueSpecification<Order_Info>();
spec &= new DirectSpecification<Order_Info>(o => o.CreateDate >= _fromDate
&& o.CreateDate <= _toDate);
return spec.SatisfiedBy();
}
}
/// <summary>
/// 通过用户信息得到他的订单列表
/// </summary>
public class OrderFromUserSpecification : Specification<Order_Info>
{
int _userID = default(Int32);
public OrderFromUserSpecification(int userID)
{
_userID = userID;
}
public override global::System.Linq.Expressions.Expression<Func<Order_Info, bool>> SatisfiedBy()
{
Specification<Order_Info> spec = new TrueSpecification<Order_Info>();
spec &= new DirectSpecification<Order_Info>(o => o.UserID == _userID);
return spec.SatisfiedBy();
}
}
业务层真实的查询主体,只要在一个方法里写就OK了,然后它非常稳定,如果以后还有其它查询业务出来,直接添加一个查询规约即可
/// <summary>
/// 根据WEB层传来及组件好的规约,返回集体
/// </summary>
/// <param name="spec"></param>
/// <returns></returns>
public List<Order_Info> GetOrder_InfoBySpec(ISpecification<Order_Info> spec)
{
return _iOrderRepository.GetEntities(spec).ToList();
}
WEB层
Web层建立一个指定的规约,并为规约组件所需要的数据即可
public ActionResult List(int? userID)
{
ISpecification<Order_Info> spec = new OrderFromUserSpecification(userID ?? );
var model = orderService.GetOrder_InfoBySpec(spec);
return View(model);
}
如果这时来了个新需要,使用用户名进行查询,你可以直接建立一个OrderFromUserNameSpecification的规约即可,而不需要修改OrderService,呵呵!
EF架构~引入规约(Specification)模式,让程序扩展性更强的更多相关文章
- EF架构~基于EF数据层的实现
回到目录 之前写过关于实现一个完整的EF架构的文章,文章的阅读量也是满大的,自己很欣慰,但是,那篇文章是我2011年写的,所以,技术有些不成熟,所以今天把我的2014年写的EF底层架构公开一下,这个架 ...
- C# 嵌入dll 动软代码生成器基础使用 系统缓存全解析 .NET开发中的事务处理大比拼 C#之数据类型学习 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持 基于EF Core的Code First模式的DotNetCore快速开发框架 【懒人有道】在asp.net core中实现程序集注入
C# 嵌入dll 在很多时候我们在生成C#exe文件时,如果在工程里调用了dll文件时,那么如果不加以处理的话在生成的exe文件运行时需要连同这个dll一起转移,相比于一个单独干净的exe,这种形 ...
- EF Core 的 Code First 模式
0 前言 本文正文第一节,会对 Code First 进行基本的介绍,以及对相关名词进行说明,读者一开始可以不用在这里消耗过多时间,可以先操作一遍例子,再回过头理解. 第二节,以一个简单的例子,展示 ...
- EF架构~CodeFirst数据迁移与防数据库删除
回到目录 本文介绍两个概念,防数据库自动删除,这是由于在code first模式下,当数据实体发生变化时,会对原来数据库进行删除,并将新数据表添加进来,但这对于我们的运营环境数据库,是万万不能接受的, ...
- EF架构~CodeFirst生产环境的Migrations
回到目录 Migrations即迁移,它是EF的code first模式出现的产物,它意思是说,将代码的变化反映到数据库上,这种反映有两种环境,一是本地开发环境,别一种是服务器的生产环境,本地开发环境 ...
- EF架构~codeFirst从初始化到数据库迁移
一些介绍 CodeFirst是EntityFrameworks的一种开发模式,即代码优先,它以业务代码为主,通过代码来生成数据库,并且加上migration的强大数据表比对功能来生成数据库版本,让程序 ...
- 基于EF Core的Code First模式的DotNetCore快速开发框架
前言 最近接了几个小单子,因为是小单子,项目规模都比较小,业务相对来说,也比较简单.所以在选择架构的时候,考虑到效率方面的因素,就采取了asp.net+entity framework中的code f ...
- 【基于EF Core的Code First模式的DotNetCore快速开发框架】完成对DB First代码生成的支持
前言 距离上一篇文章<基于EF Core的Code First模式的DotNetCore快速开发框架>已过去大半个年头,时光荏苒,岁月如梭...比较尴尬的是,在这大半个年头里,除了日常带娃 ...
- .NET应用架构设计—表模块模式与事务脚本模式的代码编写
阅读目录: 1.背景介绍 2.简单介绍表模块模式.事务脚本模式 3.正确的编写表模块模式.事务脚本模式的代码 4.总结 1.背景介绍 要想正确的设计系统架构就必须能正确的搞懂每个架构模式的用意,而不是 ...
随机推荐
- K/3 Cloud开发之旅--环境准备篇
K/3 Cloud是金蝶软件新推出的一款产品,介绍我就不多说了,谁用谁知道啊,那么我们如果要基于它做开发需要什么环境呢 开发环境必备软件 1 操作系统Windows X86/X64 或者Windows ...
- JS-offsetParent定位父节点
offsetParent:离当前元素最激动呢一个有定位的父节点 如果没有定位父级,默认是body IE7以下如果当前元素没有定位默认是body,如果有定位就是html IE7以下,如果当前元素的某个父 ...
- Python读取文本,输出指定中文(字符串)
因业务需求,需要提取文本中带有检查字样的每一行. 样本如下: 1 投入10kVB.C母分段820闭锁备自投压板 2 退出10kVB.C母分段820备投跳803压板 3 退出10kVB.C母分段820备 ...
- Java/Android 二进制数据与String互转
将经过加密的二进制数据保存到本地的方法 byte[] src = new byte[] { 122,-69, -17, 92, -76, 52, -21, -87, -10, 105, 76, -75 ...
- EOS -- 一种灵巧的系统运行跟踪模块
EOS到底是什么词的缩写,我猜应该是Error of System.最早接触它,是在UT那会.不过那会它是被设计成一个很大的数组,也没有被包含调用函数和行号,又或是时间,只是些计数.编码时,加减一个E ...
- nodejs中Stream的理解
在nodejs中可以通过fs模块读写文件,我们来看下fs模块提供的接口: fs.readFile(filename, callback) 异步读取文件. filename是读取文件的文件名,如果是相对 ...
- uva 11401 Triangle Counting
// uva 11401 Triangle Counting // // 题目大意: // // 求n范围内,任意选三个不同的数,能组成三角形的个数 // // 解题方法: // // 我们设三角巷的 ...
- Python成长笔记 - 基础篇 (八)
socket编程 应用层: 表示层: 会话层: 传输层: 网络层: ip地址 数据链路层: MAC地址 物理层: 协议类型: TCP/IP协议:三次握手,四次断开 2. Socket 参数 ...
- VS2008 工程只生成dll不生成lib的解决方案
http://topic.csdn.net/u/20081216/22/b12d1450-7585-4c9f-867a-7c181737c328.html 问题:vs2008版本的,不知道为什么只生成 ...
- Memcached & Redis使用
Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached ...