原文:通用查询实现方案(可用于DDD)[附源码] -- 简介

【声明】
写作不易,转载请注明出处(http://www.cnblogs.com/wiseant/p/3985353.html)。
 
【系列文章】
通用查询实现方案(可用于DDD)[附源码] -- 代码解读
 
【前言】
最近一直在关注和学习DDD,园子里大牛们的博文也看了不少,如dax.netnetfocus老赵田园里的蟋蟀深蓝医生冰麟轻武,不一一列举,感谢你们的无私分享。
关于DDD我一直也是看得多,做得少,可以说未入门,自然也写不出什么东西。但是我对DDD相关的思考倒并不少,特别是对于查询好像没有一个很优雅的实现方案。
上个月dax.net写了一篇关于统一查询实现思路的文章:一种通用查询语言的定义与实践,给了我动力和灵感,感谢dax.net兄。
缘于这个契机,我开始尝试实现一个通用查询方案,最近一直在做NC5.7的二次开发,只能用业余时间来搞这个,经过半个多月的努力,实现了一个 自我感觉还算优雅的通用查询方案。但一个人的智慧始终是有限的,而且本人也是半路出家,底子比较薄,遂将项目开源出来,一方面是希望可以帮助到初学者,以 便更多人来检验此查询方案的通用性和可靠性,另外主要也希望得到园子里大牛们的指点,使此查询方案得到更一步发展和提炼而日趋成熟。
PS:顺便吐槽一下NC-UAP5.7开发平台,说文明点就是太难用了,说得不文明点那就是一坨屎!用友还枉称什么国内一流的软件供应商
 
【功能】
以前一直从事基于数据库的企业应用开发,用户经常需要在客户端灵活的进行数据检索,我采用的是拼SQL的方式,虽然有各种弊端,但好处是相当自 由。到了DDD发现拼SQL基本上是行不通了,很多开发人员都采用在仓储中定义类似FindByKey()、FindByName()、 FindByNameAndAge()这样的方法来提供数据检索,每当用户需要多一种条件组合时都要改很多地方,大费周章。我希望这个查询对象定义可以有 代码检查功能,条件组合灵活多样,可以在前后台之前传输,并且一次定义就可以被编译为不同的目标格式,如SQL、Lambda表达式。
下面分别介绍这些功能如何使用。
一、直观的定义查询对象
1)支持以Linq表达式定位属性
 
如上图,首先我们以传统的方式定义了两个字段,紧接着我们以Linq表达式方式直接获取了这两个字段,经过用单元测试方法来验证两种方式所获取的字段是等同的。
显然第二种方式更优雅简洁,更重要的是当字段改名或更改了字段类型时,第一种方式编译依然可以通过,而运行时将不可避免的产生异常,导致程序维 护难度增加;而第二种方式在遇到字段改名时将在编译报告错误,如果你使用ReSharp之类工具的代码重构功能的话,更是会自动做出相应修改。
鉴于以上原因,我将在后续示例代码中仅采用第二种方式书写,我推荐你也使用第二种方式。
2)支持计算公式
如果仅仅支持对字段的比较显然是不够的,我希望日常80%~90%对查询的需求可以通过此通用查询方案来解决。
显然支持计算公式和括号优先级是非常必要的,我们通过下面这张代码贴图可以了解一个使用算式的简单例子。
 
3)支持导航属性条件
还有一个很有必要支持的功能是对导航属性的条件查询,在关系数据库中即为关联表条件查询。
第一张代码贴图中其实已经有相关代码展示了,我将该代码行单独剪切出来放在下图,图中代码行定义一个"订单.供应商.编码"字段,后面的查询示例中会有更详细的代码展示。
 
4)支持括号优先级
上面已经提到过支持括号优先级的必要性,下面的代码将展示定义一个加法运算,并将这个算式用括号括起来,保证此算式优先计算。
 
二、查询对象可序列化及反序列化
无论是DDD还是传统的三层架构都涉及到分层传输的问题,那么我们的通用查询方案也必须提供序列化及反序列化的功能,通过下面的代码贴图可以看到对XML格式文本的序列化支持,由于时间所限我将在稍后提供对json格式的序列化支持。
 
三、定义好查询后可同时编译为SQL或lambda表达式
如果前面的代码你能读懂(其实不难),那么下面的代码有很详细的注释,相信对你也没什么难度,为节省时间,我就不费口舌了。
         [Fact]
public void TestQuery3()
{
var helper = new TypeInfoHelper<Order>();
//获得订单集合中的第一张订单,用于之后的单元测试验证
var findOrder = orders.First();
//定义一个查询,条件为订单主键等于前面获取的订单对象的主键值
var query = new Query(typeof(Order))
{
RootExpression = helper.GetProperty(p => p.BillId).EqualTo(findOrder.BillId)
};
/*第一种编译方式*/
//定义一个生成Lambda表达式的编译器对象
var compiler = new LambdaExpressionCompiler<Order>(query);
//将查询对象编译为Linq表达式
var expression = compiler.Compile();
//对订单集合执行linq查询
var items = orders.Where(expression.Compile()).ToList();
//验证查询结果中仅包含一张订单,且订单编号与参照订单的编号相同
Assert.Equal(items.Count, );
Assert.Equal(items.First().BillNo, findOrder.BillNo); /*第二种编译方式*/
//定义一个生成SQL脚本的编译器对象
var sqlCompiler = new SqlWhereClauseCompiler(query) { GenSelectPart = true };
//将查询对象编译为SQL脚本
var sql = sqlCompiler.Compile();
Console.WriteLine(sql);
//执行sql脚本
var reader = dbHelper.Read(sql, sqlCompiler.ParameterValues);
//验证返回的结果中只有一条记录,且订单编号与参照订单的编号相同
Assert.Equal(reader.Read(), true);
Assert.Equal(findOrder.BillNo, reader["BillNo"]);
Assert.Equal(reader.Read(), false);//验证只能执行读取一次操作
}
【用途】
1.适用于DDD中聚合根的查询
2.适用于普通的面向数据表及视图的查询
3.可以很方便的实现可重用的面向最终用户自助查询(条件组合完全由用户来指定)
受时间所限,这部分内容如有必要后续再补充吧,写文章确实太费时间。
 
我不太擅长写作,导致大家看完博文后,可能还是很难明白此通用查询具体能做什么,补充一个稍复杂的应用示例吧。
下面的定义的查询对象编译后将生成SQL脚本(为了增强阅读性,我添加了换行和缩进):
SELECT * FROM xzcOrder AS o
WHERE (o.BillNo LIKE 'PO%' OR NOT o.BillDate >= '2014-1-1 0:00:00' )
AND EXISTS(SELECT 1 FROM supplier WHERE supplier.Id=o.SupplierId AND supplier.Code LIKE '%X%')
AND o.Invalid = 0
AND EXISTS(SELECT 1 FROM Items AS d WHERE d.BillId=o.BillId AND EXISTS(SELECT 1 FROM Product AS p WHERE p.Id=d.ProductId AND p.Unit = '部'))
AND EXISTS(SELECT 1 FROM Items AS d WHERE d.BillId=o.BillId AND d.Qty < o.TotalQty)

相应的C#代码

             var helper = new TypeInfoHelper<Order>();
var query = new Query(typeof(Order))
{
RootExpression = helper.GetProperty(p => p.BillNo).StartsWith("PO") //单号以PO打头
.Or(helper.GetProperty(p => p.BillDate).GreaterThanOrEqualTo(new DateTime(, , )).Not())//订单日期 不 大于等于 2014-1-1
.Unitary() //上面两个条件设为独立整体,即用()号包围
.And(helper.GetProperty(p => p.Supplier.Code).Contains("X"))//供应商编码中包含字符"X"
.And(helper.GetProperty(p => p.Invalid).EqualTo(false)) //订单失效标志为否
.And(helper.GetProperty(p => p.Items.FirstOrDefault().Product.Unit).EqualTo("部")) //订购产品的计量单位为"部"
.And(
//订单明细中各项订购数量小于订单总订购数量,仅为测试,无实际意义
helper.GetProperty(p => p.Items.FirstOrDefault().Qty)
.LessThan(helper.GetProperty(p => p.TotalQty)))
};
List<Order> items;
var sqlCompiler = new SqlWhereClauseCompiler(query) { GenSelectPart = true };
var sql = sqlCompiler.Compile();
Console.WriteLine(sql);
 
【源码下载】
很多人感兴的可能都是有没有提供源码下载,特别是初学者;我想说的有源码拿在手上固然是有一种踏实的感觉,但是源码之外的设计思路更重要。
这个通用查询方案的源码并没有太多技术含量,设计思路也并不高大上,因为我一向都是个实用主义者。所以你完全有可能在了解我的思路之后,在此基 础上提炼升华,用更好的技术来实现你自己的通用查询方案也绝非难事,我此举开源仅为抛砖引玉,听取大家意见,博文开头也已经作出说明了。
源码我就不搞什么回贴发邮件了,但是源码还远未成熟,还有一些代码重构工作要做,另外也希望在得到园子里大牛们指点进一步完善代码后再上传到源码托管服务器上,所以这里暂时就用百度云盘分享给大家吧。
链接:http://pan.baidu.com/s/1bn7y7oF 密码:p2dg
最近更新下载
链接:http://pan.baidu.com/s/1o64u8UQ 密码:qo6i
 
代码已托管到开源中国,地址:http://git.oschina.net/xant77/Xant.Querier
 
【关于编译项目】
开发环境:Visual Studio 2013
第三方库:xunit、SQLite
将源码解压到磁盘上,打开解决方案,在"解决方案资源管理器"中显示有两个第三方库存在冲突
 
试过启用NuGet程序包还原,还原后编译能通过,但是执行时会报告缺少SQLite.Interop.dll文件,不知道有没有更好的办法, 一个可行的解决办法是:在Tester项目上点右键,选择"管理NuGet程序包...",将已安装的2个包删除,然后再联机搜索这两个包安装上去。
注意搜索sqlite时会出现很多相关的包,请选择下图这个
 
之后便可以编译通过了,Tester项目可以编译后直接运行或是在"测试资源管理器"中点击"全部运行"查看测试方法执行结果。
 
如果看不到上图的画面,请先安装xUnit.net runner for Visual Studio 2012 and 2013
建议你先阅读XunitTest.cs文件中的代码,可以对整个项目有一个直观了解。
 
【写在最后】
博文除了图片以外绝大部分文字内容都是我在上下班乘坐地铁或公交时在手机上完成的,写作不昜,如果对你还有点用处或启发的话,望不吝点,谢谢!

通用查询实现方案(可用于DDD)[附源码] -- 简介的更多相关文章

  1. (原创)通用查询实现方案(可用于DDD)[附源码] -- 简介

    [声明] 写作不易,转载请注明出处(http://www.cnblogs.com/wiseant/p/3985353.html).   [系列文章] 通用查询实现方案(可用于DDD)[附源码] -- ...

  2. (原创)通用查询实现方案(可用于DDD)[附源码] -- 设计思路

    [声明] 写作不易,转载请注明出处(http://www.cnblogs.com/wiseant/p/3988592.html).   [系列文章] 通用查询实现方案(可用于DDD)[附源码] -- ...

  3. 通用查询实现方案(可用于DDD)[附源码] -- 设计思路

    原文:通用查询实现方案(可用于DDD)[附源码] -- 设计思路 [声明] 写作不易,转载请注明出处(http://www.cnblogs.com/wiseant/p/3988592.html).   ...

  4. 开源方案搭建可离线的精美矢量切片地图服务-8.mapbox 之sprite大图图标文件生成(附源码)

    项目成果展示(所有项目文件都在阿里云的共享云虚拟主机上,访问地图可以会有点慢,请多多包涵). 01:中国地图:http://test.sharegis.cn/mapbox/html/3china.ht ...

  5. 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生

    [转].NET(C#):浅谈程序集清单资源和RESX资源   目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...

  6. 日志组件Log2Net的介绍和使用(附源码开源地址)

    Log2Net是一个用于收集日志到数据库或文件的组件,支持.NET和.NetCore平台. 此组件自动收集系统的运行日志(服务器运行情况.在线人数等).异常日志.程序员还可以添加自定义日志. 该组件支 ...

  7. Cesium专栏-填挖方分析(附源码下载)

    Cesium 是一款面向三维地球和地图的,世界级的JavaScript开源产品.它提供了基于JavaScript语言的开发包,方便用户快速搭建一款零插件的虚拟地球Web应用,并在性能,精度,渲染质量以 ...

  8. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

  9. 轻量级通信引擎StriveEngine —— C/S通信demo(2) —— 使用二进制协议 (附源码)

    在网络上,交互的双方基于TCP或UDP进行通信,通信协议的格式通常分为两类:文本消息.二进制消息. 文本协议相对简单,通常使用一个特殊的标记符作为一个消息的结束. 二进制协议,通常是由消息头(Head ...

随机推荐

  1. APP-午饭去哪吃

    走到这个快节奏的城市中.部门聚餐.朋友吃饭这些都是日常生活中时有发生的事情,往往吃的东西都是千篇一律,图的也仅仅剩下的是环境了.那么.非常纠结常常去的地方,怎么办呢?来吧.我们随机摇一个吧! wate ...

  2. 怎样在Ubuntu手机平台中开发Cordova HTML5应用

    我们知道Cordova HTML5应用具有夸平台的特性,同一时候也具有訪问本地一些资源的能力.在今天的这篇文章中.我们将介绍一下怎样创建并执行一个Cordova HTML5的应用到我们的Ubuntu手 ...

  3. nyoj--311--完全背包(动态规划,完全背包)

    完全背包 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 直接说题意,完全背包定义有N种物品和一个容量为V的背包,每种物品都有无限件可用.第i种物品的体积是c,价值是w. ...

  4. [Project Euler 409] Nim Extreme 解题报告 (统计方案数)

    题目链接:https://projecteuler.net/problem=409 题目: 题解: 题目问你必胜态的数目,我们考虑用总的方案数减去必败态的方案数(NIM游戏没有平局这个操作) 必败态的 ...

  5. [JZOJ4272] [NOIP2015模拟10.28B组] 序章-弗兰德的秘密 解题报告(树形DP)

    Description 背景介绍弗兰德,我不知道这个地方对我意味着什么.这里是一切开始的地方.3年前,还是个什么都没见过的少年,来到弗兰德的树下,走进了封闭的密室,扭动的封尘已久机关,在石板上知道了这 ...

  6. Case study: word play

    For the exercises in this chapter we need a list of English words. There are lots of word lists avai ...

  7. 基于opencv的手写数字字符识别

    摘要 本程序主要参照论文,<基于OpenCV的脱机手写字符识别技术>实现了,对于手写阿拉伯数字的识别工作.识别工作分为三大步骤:预处理,特征提取,分类识别.预处理过程主要找到图像的ROI部 ...

  8. jqueryEasyUI form表单提交的一个困惑

    今天用到了jqueryEasyUI的form表单做一个增加操作的提交,想打开调试(用的是火狐)看看传的参数,但是怎么也看不到form表单提交的http请求?而且还会发送一个另外的请求! 在页面加载时, ...

  9. BZOJ 3277/3473 广义后缀自动机

    说实话没啥难的. 建一棵广义后缀自动机,暴力自底向上更新即可. 时间复杂度非常玄学,但据说是可以过的. 要注意每个串中相同的子串的贡献是都要加进去的,开始因为这个被坑了好久 QAQ Code: #in ...

  10. 2017国家集训队作业[agc016b]Color Hats

    2017国家集训队作业[agc016b]Color Hats 题意: 有\(N\)个人,每个人有一顶帽子.帽子有不同的颜色.现在,每个人都告诉你,他看到的所有其它人的帽子共有多少种颜色,问有没有符合所 ...