有的时候,你需要动态构建一个比较复杂的查询条件,传入数据库中进行查询。而条件本身可能来自前端请求或者配置文件。那么这个时候,表达式树,就可以帮助到你。
以下是一个简单的单元测试用例。接下来,我们将这个测试用例改的面目全非。
[test]public void normal(){ var re = enumerable.range(0, 10).asqueryable() // 0-9 .where(x => x >= 1 && x < 5).tolist(); // 1 2 3 4 var expectation = enumerable.range(1, 4); // 1 2 3 4 re.should().beequivalentto(expectation);}
由于是 queryable 的关系,所以where当中的其实是一个表达式,那么我们把它单独定义出来,顺便水一下文章的长度。
[test]public void expression00(){ expression<func<int, bool>> filter = x => x >= 1 && x < 5; var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation);}
expression 右侧是一个 lambda ,所以可以捕获上下文中的变量。
这样你便可以把 minvalue 和 maxvalue 单独定义出来。
于是乎你可以从其他地方来获取 minvalue 和 maxvalue 来改变 filter。
[test]public void expression01(){ var minvalue = 1; var maxvalue = 5; expression<func<int, bool>> filter = x => x >= minvalue && x < maxvalue; var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation);}
那既然这样,我们也可以使用一个方法来创建 expression。
这个方法,实际上就可以认为是这个 expression 的工厂方法。
[test]public void expression02(){ var filter = createfilter(1, 5); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); expression<func<int, bool>> createfilter(int minvalue, int maxvalue) { return x => x >= minvalue && x < maxvalue; }}
那可以使用 minvalue 和 maxvalue 作为参数来制作工厂方法,那么用委托当然也可以。
于是,我们可以把左边和右边分别定义成两个 func,从而由外部来决定左右具体的比较方式。
[test]public void expression03(){ var filter = createfilter(x => x >= 1, x => x < 5); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); expression<func<int, bool>> createfilter(func<int, bool> leftfunc, func<int, bool> rightfunc) { return x => leftfunc.invoke(x) && rightfunc.invoke(x); }}
实际上,左右两个不仅仅是两个func,其实也可以直接是两个表达式。
不过稍微有点不同的是,表达式的合并需要用 expression 类型中的相关方法创建。
我们可以发现,调用的地方这次其实没有任何改变,因为 lambda 既可以隐式转换为 func 也可以隐式转换为 expression。
每个方法的意思可以从注释中看出。
[test]public void expression04(){ var filter = createfil十一国庆节活动策划ter(x => x >= 1, x => x < 5); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); expression<func<int, bool>> createfilter(expression<func<int, bool>> leftfunc, expression<func<int, bool>> rightfunc) { // x var pexp = expression.parameter(typeof(int), "x"); // (a => leftfunc(a))(x) var leftexp = expression.invoke(leftfunc, pexp); // (a => rightfunc(a))(x) var rightexp = expression.invoke(rightfunc, pexp); // (a => leftfunc(a))(x) && (a => rightfunc(a))(x) var bodyexp = expression.andalso(leftexp, rightexp); // x => (a => leftfunc(a))(x) && (a => rightfunc(a))(x) var resultexp = expression.lambda<func<int, bool>>(bodyexp, pexp); return resultexp; }}
使其更加简单
但是,上面的方法,其实可以再优化一下。避免对左右表达式的直接调用。
使用一个叫做 unwrap 的方法,可以将 lambda expression 解构成只包含 body 部分的表达式。
这是一个自定义的扩展方法,你可以通过objectvisitor来引入这个方法。
[test]public void expression05(){ var filter = createfilter(x => x >= 1, x => x < 5); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); expression<func<int, bool>> createfilter(expression&l成绩分析t;func<int, bool>> leftfunc, expression<func<int, bool>> rightfunc) { // x var pexp = expression.parameter(typeof(int), "x"); // leftfunc(x) var leftexp = leftfunc.unwrap(pexp); // rightfunc(x) var rightexp = rightfunc.unwrap(pexp); // leftfunc(x) && rightfunc(x) var bodyexp = expression.andalso(leftexp, rightexp); // x => leftfunc(x) && rightfunc(x) var resultexp = expression.lambda<func<int, bool>>(bodyexp, pexp); return resultexp; }}
我们可以再优化以下,把 createfilter 方法扩展为支持多个子表达式和可自定义子表达式的连接方式。
于是,我们就可以得到一个 joinsubfilters 方法。
[test]public void expression06(){ var filter = joinsubfilters(expression.andalso, x => x >= 1, x => x < 5); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); expression<func<int, bool>> joinsubfilters(func<expression, expression, expression> expjoiner, params expression<func<int, bool>>[] subfilters) { // x var pexp = expression.parameter(typeof(int), "x"); var result = subfilters[0]; foreach (var sub in subfilters[1..]) { var leftexp = result.unwrap(pexp); var rightexp = sub.unwrap(pexp); var bodyexp = expjoiner(leftexp, rightexp); result = expression.lambda<func<int, bool>>(bodyexp, pexp); } return result; }}
有了前面的经验,我们知道。其实x => x >= 1这个表达式可以通过一个工厂方法来建。
所以,苏州大学排名我们使用一个 createminvaluefilter 来创建这个表达式。
[test]public void expression07(){ var filter = joinsubfilters(expression.andalso, createminvaluefilter(1), x => x < 5); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); expression<func<int, bool>> createminvaluefilter(int minvalue) { return x => x >= minvalue; } expression<func<int, bool>> joinsubfilters(func<expression, expression, expression> expjoiner, params expression<func<int, bool>>[] subfilters) { // x var pexp = expression.parameter(typeof(int), "x"); var result = subfilters[0]; foreach (var sub in subfilters[1..]) { var leftexp = result.unwrap(pexp); var rightexp = sub.unwrap(pexp); var bodyexp = expjoiner(leftexp, rightexp); result = expression.lambda<func<int, bool>>(bodyexp, pexp); } return result; }}
当然,可以只使用 expression 相关的方法来创建x => x >= 1。
[test]public void expression08(){ var filter = joinsubfilters(expression.andalso, createminvaluefilter(1), x => x < 5); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); expression<func<int, bool>> createminvaluefilter(int minvalue) { // x var pexp = expression.parameter(typeof(int), "x"); // minvalue var rightexp = expression.constant(minvalue); // x >= minvalue var bodyexp = expression.greaterthanorequal(pexp, rightexp); var result = expression.lambda<func<int, bool>>(bodyexp, pexp); return result; } expression<func<int, bool>> joinsubfilters(func<expression, expression, expression> expjoiner, params expression<func<int, bool>>[] subfilters) { // x var pexp = expression.parameter(typeof(int), "x"); var result = subfilters[0]; foreach (var sub in subfilters[1..]) { var leftexp = result.unwrap(pexp); var rightexp = sub.unwrap(pexp); var bodyexp = expjoiner(leftexp, rightexp); result = expression.lambda<func<int, bool>>(bodyexp, pexp); } return result; }}
同理,子表达式都可以如此创建
那既然都用了 expression 来创建子表达式了,那就干脆再做一点点改进,把x => x < 5也做成从工厂方法获取。
[test]public void expression09(){ var filter = joinsubfilters(expression.andalso, createvaluecomparefilter(expression.greaterthanorequal, 1), createvaluecomparefilter(expression.lessthan, 5)); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); expression<func<int, bool>> createvaluecomparefilter(func<expression, expression, expression> comparerfunc, int rightvalue) { var pexp = expression.parameter(typeof(int), "x"); var rightexp = expression.constant(rightvalue); var bodyexp = comparerfunc(pexp, rightexp); var result = expression.lambda<func<int, bool>>(bodyexp, pexp); return result; } expression<func<int, bool>> joinsubfilters(func<expression, expression, expression> expjoiner, params expression<func<int, bool>>[] subfilters) { // x var pexp = expression.parameter(typeof(int), "x"); var result = subfilters[0]; foreach (var sub in subfilters[1..]) { var leftexp = result.unwrap(pexp); var rightexp = sub.unwrap(pexp); var bodyexp = expjoiner(leftexp, rightexp); result = expression.lambda<func<int, bool>>(bodyexp, pexp); } retur标准情人n result; }}
加入一点点配置,就完成了
最后,我们在把子表达式的创建通过一点点小技巧。通过外部参数来决定。就基本完成了一个多 and 的值比较查询条件的动态构建。
[test]public void expression10(){ var config = new dictionary<string, int> { { ">=", 1 }, { "<", 5 } }; var subfilters = config.lect(x => createvaluecomparefilter(mapconfig(x.key), x.value)).toarray(); var filter = joinsubfilters(expression.andalso, subfilters); var re = enumerable.range(0, 10).asqueryable() .where(filter).tolist(); var expectation = enumerable.range(1, 4); re.should().beequivalentto(expectation); func<expression, expression, expression> mapconfig(string op) { return op switch { ">=" => expression.greaterthanorequal, "<" => expression.lessthan, _ => throw new argumentoutofrangeexception(nameof(op)) }; } expression<func<int, bool>> createvaluecomparefilter(func<expression, expression, expression> comparerfunc, int rightvalue) { var pexp = expression.parameter(typeof(int), "x"); var rightexp = expression.constant(rightvalue); var bodyexp = comparerfunc(pexp, rightexp); var result = expression.lambda<func<int, bool>勒仕垃圾处理器;>(bodyexp, pexp); return result; } expression<func<int, bool>> joinsubfilters(func<expression, expression, expression> expjoiner, params expression<func<int, bool>>[] subfilters) { // x var pexp = expression.parameter(typeof(int), "x"); var result = subfilters[0]; foreach (var sub in subfilters[1..]) { var leftexp = result.unwrap(pexp); var rightexp = sub.unwrap(pexp); var bodyexp = expjoiner(leftexp, rightexp); result = expression.lambda<func<int, bool>>(bodyexp, pexp); } return result; }}
以上就是java动态构建数据库复杂查询实现示例的详细内容,更多关于动态构建数据库复杂查询的资料请关注www.887551.com其它相关文章!
本文发布于:2023-04-03 23:09:32,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/5f56671baa16c0be9cf2dea78b2aadda.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:java动态构建数据库复杂查询教程.doc
本文 PDF 下载地址:java动态构建数据库复杂查询教程.pdf
留言与评论(共有 0 条评论) |