40

C# 表达式树Lambda扩展(四) - 园子的蜗牛

 4 years ago
source link: https://www.cnblogs.com/snailblog/p/11525118.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本来计算这篇文章在后面需要运用的时候写的,但是既然写到表达式的扩展呢,就一起写完吧。

看到这个标题就有一种疑问,Lambda表达式本来就是表达式树,还需要怎么扩展?那就看看下面的内容,你就知道了。

表达式系列目录

C# 表达式树讲解(一)

C# 表达式树遍历(二)

C# 表达式树分页扩展(三)

C# 表达式树Lambda扩展(四)

二、Lambda扩展

这里先不忙解答上面的问题,我们先看下这样一个应用场景。

一个页面的请求,里面带有一些条件查询,请求类如下

public class ScoreRequest
{
    public string CourseName { get; set; }
    public string StudentName { get; set; }
}

要求查询与课程名称和学生名称匹配的数据

数据源我们就以上一例子的数据源

ContractedBlock.gifExpandedBlockStart.gif

View Code

ContractedBlock.gifExpandedBlockStart.gif

View Code

好了现在我们就查询数据

var request = new ScoreRequest()
            {
                CourseName = "数",
                StudentName = "H"
            };
            var resultDatas = datas.Where(e => e.CourseName.Contains(request.CourseName) && e.StudentName.Contains(request.StudentName))
                .ToList();

如果查询对象里面CourseName和StudentName字段都有值得话,这样写没问题。如果没值,那就最后的数据,就不准确了。

如果是直接拼凑sql语句,我们可以用if(String.IsNullOrEmpty())来判断,但是现在判断了,怎么拼凑Lambda表达式呢?

所以就需要我们对Lambda表达式进行扩展,让他支持这种情况。那上面的问题,就不用再专门回答了吧!!!!

创建一个LambdaExtension的类,代码如下

public static class LambdaExtension
{
    public static Expression<Func<T, bool>> True<T>() { return param => true; }
    public static Expression<Func<T, bool>> False<T>() { return param => false; }
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.AndAlso);
    }
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.OrElse);
    }
    private static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
    {
        var map = first.Parameters
            .Select((f, i) => new { f, s = second.Parameters[i] })
            .ToDictionary(p => p.s, p => p.f);
        var secondBody = PFTParameterExtension.ReplaceParameters(map, second.Body);
        return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    }

    private class PFTParameterExtension : ExpressionVisitor
    {
        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        public PFTParameterExtension()
        {

        }

        public PFTParameterExtension(Dictionary<ParameterExpression, ParameterExpression> map)
        {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }

        /// <summary>
        /// 替换参数
        /// </summary>
        /// <param name="map">The map.</param>
        /// <param name="exp">The exp.</param>
        /// <returns>Expression</returns>
        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
        {
            return new PFTParameterExtension(map).Visit(exp);
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            ParameterExpression replacement;
            if (map != null && map.Count > 0 && map.TryGetValue(p, out replacement))
            {
                p = replacement;
            }
            return base.VisitParameter(p);
        }

    }

}

这里面私有化了一个表达式树访问器,他的作用主要是用来同步Lambda表达式里面的参数。

下面是调用方式

            var expression = LambdaExtension.True<ScoreClass>();
            if (!string.IsNullOrWhiteSpace(request.CourseName))
                expression = expression.And(e => e.CourseName.Contains(request.CourseName));
            if (!string.IsNullOrWhiteSpace(request.StudentName))
                expression = expression.And(et => et.StudentName.Contains(request.StudentName));

            var resultDatas = datas.Where(expression.Compile())
                .ToList();
            Console.WriteLine($"查询结果:\n{string.Join("\n", resultDatas.Select(e => $"{e.StudentName} {e.CourseName} {e.Score}"))}");

where条件里面只能带委托,而我们的expression是Lambda表达式,所以需要Compile进行委托编译。

运行结果:

image

仔细看代码,第一个条件And里面的参数是“e”,第二个条件里面的参数是et,同一个Lambda表达式里面(这里只有一个参数),参数肯定是一致的,所以在LambdaExtension类中,在合并两个Lambda表达式的时候,就需要将参数合并成一个。

经过这样的扩展,我们就可以根据我们的实际情况,拼凑好需要的表达式,得到我们想要的结果。

表达式树方面的讲解,终于可以告一段落了。一直后没有这样的写文章,现在觉得写文章还是真的挺累的,今年中秋节的这三天,算是全部的给博客园了。不过这三天讲解的内容,基本上把后面Dapper的扩展需要用的技术都铺垫了,后面我们就继续对ORM的讲解了。其实没写一篇博文,蜗牛都会去罗列和梳理相关知识点,这也让蜗牛获益匪浅,也希望蜗牛的博客能帮助到园友,这就是所谓的“赠人玫瑰,手留余香”吧。

    如果您觉得阅读本文对您有帮助,请点一下右下角推荐”按钮,博主在此感谢!另外您也可以选择关注我,可以很方便找到我!
    感谢您花时间阅读此篇文章,如果您觉得看了这篇文章之后心情还比较高兴,可以打赏一下,请博主喝上一杯咖啡,让博主继续码字……
    本文版权归作者和博客园共有,来源网址:https://www.cnblogs.com/snailblog 欢迎各位转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK