搜索

查看: 3041|回复: 11

[ASP.NET] .NET中lambda表达式合并问题及解决方法

[复制链接]
发表于 2023-5-4 11:32:30 | 显示全部楼层 |阅读模式
Editor 2023-5-4 11:32:30 3041 11 看全部
目录
  • 解决方案:
  • 完美解决事情的起因是公司一个小伙子问了我个问题 “海哥,来帮我看下这段代码怎么不行”
    Func nameFilter = x=>x.Name == "test";
    DbContext.Report.Where(x=>x.State==1 && nameFilter(x));
    我一看,好家伙,这么骚的代码都能让你想出来,正常情况下用Linq To Object是可以这么操作的,但是EF的IQueryable查询是不能这么操作的。
    Linq To Object是直接执行表达式,他就是个委托方法,里面嵌套多少层委托和方法都是能直接执行的
    IQueryable并不会执行表达式和方法,是把表达式转换为对应的Sql语句来执行,解析到nameFilter的时候他就懵逼了,这是啥玩意儿啊,sql里面没有这种东西啊,他就转换不了了。
    小伙子知道后明细很失望,那不能啊,也不是我想显摆我的技术,就是想让小伙子能继续他的骚操作,给他来点海克斯科技与狠活。

    解决方案:
    //表达式
    Func nameFilter = x=>x.Name == "test";
    Func stateFilter = x=>x.State==1;
    //合并为
    Func whereFilter = x=>x.Name == "test" && x.State==1;
    //调用
    DbContext.Report.Where(whereFilter);
    完美解决
    那怎么合并,当然得自己构造一个新的表达式,构造表达式需要用到Expression类,如果没有用过这个类,可以按照下面的方式来调试看看一个表达式转换为表达式树是怎么样的。
    TestExpression(x=>x.Name == "test",x=>x.State==1);
    public static void TestExpression(Expression> left,Expression> right)
    {
        //调试查看expression对象
        var bodyLeft = left.Body;//这个就是x.Name == "test"
        var bodyRight = right.Body;//这个就是x.State==1
    }
    好,这里我们能获取到表达式的Body,然后使用Expression类能很好的合并两个表达式的body
    var andAlso = Expression.AndAlso(bodyLeft ,bodyRight);//x.Name == "test" && x.State==1
    这样还不行,这两个表达式是两个不同的委托对象,他们的参数x也是两个不同的对象,合并了又没完全合并
    这就需要用到ExpressionVisitor类来递归表达式树,把两个表达式的参数替换为同一个参数。
        ///
        /// 替换表达式参数
        ///
        public class ReplaceExpressionVisitor : ExpressionVisitor
        {
            private Expression _leftParameter;
            public ReplaceExpressionVisitor(Expression leftParameter)
            {
                _leftParameter= leftParameter;
            }
            
            protected override Expression VisitParameter(ParameterExpression node)
            {
                return _leftParameter;
            }
        }
    最终
    TestExpression(x=>x.Name == "test",x=>x.State==1);
    public static void TestExpression(Expression> left,Expression> right)
    {
        //调试查看expression对象
        var bodyLeft = left.Body;//这个就是x.Name == "test"
        var bodyRight = right.Body;//这个就是x.State==1
        var leftParameter = left.Parameters[0];
        //表达式递归访问
        var visitor =new ReplaceExpressionVisitor(leftParameter);
        //替换参数
        bodyRight = visitor.Visit(bodyRight);
        //合并表达式
        var expression = Expression.AndAlso(bodyLeft , bodyRight);
        //构建表达式
        var whereExpression= Expression.Lambda>(expression , left.Parameters);
        //编译表达式
        var whereFilter = whereExpression.Compile();
        //使用
        DbContext.Report.Where(whereFilter);
    }
    正想给小老弟显摆一下的时候,他又去写其他骚代码了
    骚不过骚不过,完善一下列子,下面是完整的代码
    小嫩手不想动的小伙伴可以直接nuget上查找DynamicExpression.Core,直接使用
    更多源码看本人github
        ///
        /// 替换表达式参数
        ///
        public class ReplaceExpressionVisitor : ExpressionVisitor
        {
            private Dictionary _parameters;
            public ReplaceExpressionVisitor(Dictionary parameters)
            {
                _parameters = parameters;
            }
            protected override Expression VisitParameter(ParameterExpression node)
            {
                if (_parameters.TryGetValue(node, out Expression _newValue))
                {
                    return _newValue;
                }
                return base.Visit(node);
            }
        }
        ///
        /// 表达式扩展
        ///
        public static class ExpressionExtension
        {
            ///
            /// 使用AndAlso合并表达式
            ///
            ///
            ///
            public static Expression AndAlso(this IList> exprs)
            {
                if (exprs.Count == 0) return null;
                if (exprs.Count == 1) return exprs[0];
                var leftExpr = exprs[0];
                var left = leftExpr.Body;
                for (int i = 1; i (left, leftExpr.Parameters);
            }
            ///
            /// 使用AndAlso合并表达式
            ///
            ///
            ///
            ///
            /// left AndAlso right
            public static Expression AndAlso(this Expression left, Expression right)
            {
                return AndAlso(new List>() { left, right });
            }
            ///
            /// 使用OrElse合并表达式
            ///
            ///
            ///
            ///
            public static Expression OrElse(this IList> exprs)
            {
                if (exprs.Count == 0) return null;
                if (exprs.Count == 1) return exprs[0];
                var leftExpr = exprs[0];
                var left = leftExpr.Body;
                for (int i = 1; i (left, leftExpr.Parameters);
            }
            ///
            /// 使用OrElse合并表达式
            ///
            ///
            ///
            ///
            /// left OrElse right
            public static Expression OrElse(this Expression left, Expression right)
            {
                return OrElse(new List>() { left, right });
            }
            ///
            /// 构建visitor
            ///
            ///
            ///
            ///
            private static ReplaceExpressionVisitor GetReplaceExpressionVisitor(ReadOnlyCollection oldParameters, ReadOnlyCollection newParameters)
            {
                Dictionary dic = new Dictionary();
                for (int i = 0; i
    使用
    string connectString = "Data Source=.;Initial Catalog=RportTest;Integrated Security=True";
    var optionsBuilder = new DbContextOptionsBuilder();
    optionsBuilder.UseSqlServer(connectString);
    using (TestContext ctx = new TestContext(optionsBuilder.Options))
    {
        Expression> epxr1 = report => report.ID == 2023;
        Expression> epxr2 = report => report.Name == "test1";
        var epxr3 = new List>>() { epxr1, epxr2 };
        var andPredicate = epxr3.AndAlso();
        var andQuery = ctx.ReportData.Where(andPredicate);
        string andSql = andQuery.ToQueryString();
        var andResult = andQuery.ToList();
        var orPredicate = epxr3.OrElse();
        var orQuery = ctx.ReportData.Where(orPredicate);
        string orSql = orQuery.ToQueryString();
        var orResult = orQuery.ToList();
    }
    到此这篇关于.net lambda表达式合并的文章就介绍到这了,更多相关.net lambda表达式内容请搜索知鸟论坛以前的文章或继续浏览下面的相关文章希望大家以后多多支持知鸟论坛
  • 回复

    使用道具 举报

    发表于 2023-6-28 21:21:38 | 显示全部楼层
    井底燕雀傥 2023-6-28 21:21:38 看全部
    感谢楼主的无私分享!要想知鸟论坛好 就靠你我他
    回复

    使用道具 举报

    发表于 2023-6-28 23:54:46 | 显示全部楼层
    伊索谗言 2023-6-28 23:54:46 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 08:19:59 | 显示全部楼层
    丁侦球 2023-6-29 08:19:59 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-29 11:06:51 | 显示全部楼层
    123456833 2023-6-29 11:06:51 看全部
    感谢楼主的无私分享!要想知鸟论坛好 就靠你我他
    回复

    使用道具 举报

    发表于 2023-6-29 15:33:25 | 显示全部楼层
    无人岛屿颈 2023-6-29 15:33:25 看全部
    楼主,大恩不言谢了!知鸟论坛是最棒的!
    回复

    使用道具 举报

    发表于 2023-6-30 00:33:48 | 显示全部楼层
    当当当当裤裆坦 2023-6-30 00:33:48 看全部
    这东西我收了!谢谢楼主!知鸟论坛真好!
    回复

    使用道具 举报

    发表于 2023-6-30 09:15:50 | 显示全部楼层
    dxf17 2023-6-30 09:15:50 看全部
    我看不错噢 谢谢楼主!知鸟论坛越来越好!
    回复

    使用道具 举报

    发表于 2023-6-30 18:03:01 | 显示全部楼层
    计划你大爷计j 2023-6-30 18:03:01 看全部
    楼主发贴辛苦了,谢谢楼主分享!我觉得知鸟论坛是注册对了!
    回复

    使用道具 举报

    发表于 2023-7-1 00:43:12 | 显示全部楼层
    123456868 2023-7-1 00:43:12 看全部
    楼主发贴辛苦了,谢谢楼主分享!我觉得知鸟论坛是注册对了!
    回复

    使用道具 举报

    • 您可能感兴趣
    点击右侧快捷回复 【请勿灌水】
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则 返回列表

    RSS订阅| SiteMap| 小黑屋| 知鸟论坛
    联系邮箱E-mail:zniao@foxmail.com
    快速回复 返回顶部 返回列表