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.适用于普通的面向数据表及视图的查询 受时间所限,这部分内容如有必要后续再补充吧,写文章确实太费时间。 我不太擅长写作,导致大家看完博文后,可能还是很难明白此通用查询具体能做什么,补充一个稍复杂的应用示例吧。 下面的定义的查询对象编译后将生成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#代码
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 【关于编译项目】 开发环境:Visual Studio 2013 第三方库:xunit、SQLite 扩展工具:xUnit.net runner for Visual Studio 2012 and 2013 将源码解压到磁盘上,打开解决方案,在"解决方案资源管理器"中显示有两个第三方库存在冲突 试过启用NuGet程序包还原,还原后编译能通过,但是执行时会报告缺少SQLite.Interop.dll文件,不知道有没有更好的办法, 一个可行的解决办法是:在Tester项目上点右键,选择"管理NuGet程序包...",将已安装的2个包删除,然后再联机搜索这两个包安装上去。 注意搜索sqlite时会出现很多相关的包,请选择下图这个 之后便可以编译通过了,Tester项目可以编译后直接运行或是在"测试资源管理器"中点击"全部运行"查看测试方法执行结果。 如果看不到上图的画面,请先安装xUnit.net runner for Visual Studio 2012 and 2013 建议你先阅读XunitTest.cs文件中的代码,可以对整个项目有一个直观了解。 【写在最后】 博文除了图片以外绝大部分文字内容都是我在上下班乘坐地铁或公交时在手机上完成的,写作不昜,如果对你还有点用处或启发的话,望不吝点赞,谢谢!