这篇文章次要介绍了java动静构建数据库简单查问的实现示例,有须要的敌人能够借鉴参考下,心愿可能有所帮忙,祝大家多多提高,早日升职加薪
有的时候,你须要动静构建一个比较复杂的查问条件,传入数据库中进行查问。而条件自身可能来自前端申请或者配置文件。那么这个时候,表达式树,就能够帮忙到你。
Where当中能够传入固定的条件
以下是一个简略的单元测试用例。接下来,咱们将这个测试用例改的面目全非。

[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就是一种表达式树
因为是 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);}

表达式能够通过Lambda隐式转换
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;    }}

通过Func能够更加灵便的组合条件
那能够应用 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 = 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<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<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手动创立
当然,能够只应用 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);        }         return result;    }}

退出一点点配置,就实现了

最初,咱们在把子表达式的创立通过一点点小技巧。通过内部参数来决定。就根本实现了一个多 And 的值比拟查问条件的动静构建。

[Test]public void Expression10(){    var config = new Dictionary<string, int>    {        { ">=", 1 },        { "<", 5 }    };    var subFilters = config.Select(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;    }}

以上就是本文全副的内容,心愿能帮忙到大家!