(原创)通用查询实现方案(可用于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、直观的定义查询对象
1)支持以Linq表达式定位属性
 
如上图,首先咱们以传统的方式定义了两个字段,紧接着咱们以Linq表达式方式直接获取了这两个字段,通过用单元测试方法来验证两种方式所获取的字段是等同的。
显然第二种方式更优雅简洁,更重要的是当字段更名或更改了字段类型时,第一种方式编译依然能够经过,而运行时将不可避免的产生异常,致使程序维 护难度增长;而第二种方式在遇到字段更名时将在编译报告错误,若是你使用ReSharp之类工具的代码重构功能的话,更是会自动作出相应修改。
鉴于以上缘由,我将在后续示例代码中仅采用第二种方式书写,我推荐你也使用第二种方式。
2)支持计算公式
若是仅仅支持对字段的比较显然是不够的,我但愿平常80%~90%对查询的需求能够经过此通用查询方案来解决。
显然支持计算公式和括号优先级是很是必要的,咱们经过下面这张代码贴图能够了解一个使用算式的简单例子。
 
3)支持导航属性条件
还有一个颇有必要支持的功能是对导航属性的条件查询,在关系数据库中即为关联表条件查询。
第一张代码贴图中其实已经有相关代码展现了,我将该代码行单独剪切出来放在下图,图中代码行定义一个"订单.供应商.编码"字段,后面的查询示例中会有更详细的代码展现。
 
4)支持括号优先级
上面已经提到过支持括号优先级的必要性,下面的代码将展现定义一个加法运算,并将这个算式用括号括起来,保证此算式优先计算。
 
2、查询对象可序列化及反序列化
不管是DDD仍是传统的三层架构都涉及到分层传输的问题,那么咱们的通用查询方案也必须提供序列化及反序列化的功能,经过下面的代码贴图能够看到对XML格式文本的序列化支持,因为时间所限我将在稍后提供对json格式的序列化支持。
 
3、定义好查询后可同时编译为SQL或lambda表达式
若是前面的代码你能读懂(其实不难),那么下面的代码有很详细的注释,相信对你也没什么难度,为节省时间,我就不费口舌了。
 1         [Fact]
 2         public void TestQuery3()
 3         {
 4             var helper = new TypeInfoHelper<Order>();
 5             //得到订单集合中的第一张订单,用于以后的单元测试验证
 6             var findOrder = orders.First();
 7             //定义一个查询,条件为订单主键等于前面获取的订单对象的主键值
 8             var query = new Query(typeof(Order))
 9             {
10                 RootExpression = helper.GetProperty(p => p.BillId).EqualTo(findOrder.BillId)
11             };
12             /*第一种编译方式*/
13             //定义一个生成Lambda表达式的编译器对象
14             var compiler = new LambdaExpressionCompiler<Order>(query);
15             //将查询对象编译为Linq表达式
16             var expression = compiler.Compile();
17             //对订单集合执行linq查询
18             var items = orders.Where(expression.Compile()).ToList();
19             //验证查询结果中仅包含一张订单,且订单编号与参照订单的编号相同
20             Assert.Equal(items.Count, 1);
21             Assert.Equal(items.First().BillNo, findOrder.BillNo);
22 
23             /*第二种编译方式*/
24             //定义一个生成SQL脚本的编译器对象
25             var sqlCompiler = new SqlWhereClauseCompiler(query) { GenSelectPart = true };
26             //将查询对象编译为SQL脚本
27             var sql = sqlCompiler.Compile();
28             Console.WriteLine(sql);
29             //执行sql脚本
30             var reader = dbHelper.Read(sql, sqlCompiler.ParameterValues);
31             //验证返回的结果中只有一条记录,且订单编号与参照订单的编号相同
32             Assert.Equal(reader.Read(), true);
33             Assert.Equal(findOrder.BillNo, reader["BillNo"]);
34             Assert.Equal(reader.Read(), false);//验证只能执行读取一次操做
35         }

 

【用途】
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#代码html

 1             var helper = new TypeInfoHelper<Order>();
 2             var query = new Query(typeof(Order))
 3             {
 4                 RootExpression = helper.GetProperty(p => p.BillNo).StartsWith("PO") //单号以PO打头
 5                     .Or(helper.GetProperty(p => p.BillDate).GreaterThanOrEqualTo(new DateTime(2014, 1, 1)).Not())//订单日期 不 大于等于 2014-1-1
 6                     .Unitary() //上面两个条件设为独立总体,即用()号包围
 7                     .And(helper.GetProperty(p => p.Supplier.Code).Contains("X"))//供应商编码中包含字符"X"
 8                     .And(helper.GetProperty(p => p.Invalid).EqualTo(false)) //订单失效标志为否
 9                     .And(helper.GetProperty(p => p.Items.FirstOrDefault().Product.Unit).EqualTo("")) //订购产品的计量单位为"部"
10                     .And(
11                         //订单明细中各项订购数量小于订单总订购数量,仅为测试,无实际意义
12                         helper.GetProperty(p => p.Items.FirstOrDefault().Qty)
13                             .LessThan(helper.GetProperty(p => p.TotalQty)))
14             };
15             List<Order> items;
16             var sqlCompiler = new SqlWhereClauseCompiler(query) { GenSelectPart = true };
17             var sql = sqlCompiler.Compile();
18             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文件中的代码,能够对整个项目有一个直观了解。
 
【写在最后】
博文除了图片之外绝大部分文字内容都是我在上下班乘坐地铁或公交时在手机上完成的,写做不昜,若是对你还有点用处或启发的话,望不吝点 ,谢谢!
相关文章
相关标签/搜索