首页 > 其他分享 >DynamicExpression.Core

DynamicExpression.Core

时间:2023-02-19 17:24:03浏览次数:42  
标签:Core right DynamicExpression exprs var Expression 表达式 left

DynamicExpression.Core

.net lambda表达式合并

 

事情的起因是公司一个小伙子问了我个问题 “海哥,来帮我看下这段代码怎么不行”

Func<Report,bool> 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<Report,bool> nameFilter = x=>x.Name == "test";
Func<Report,bool> stateFilter = x=>x.State==1;
//合并为
Func<Report,bool> 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<Func<Report, bool>> left,Expression<Func<Report, bool>> 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类来递归表达式树,把两个表达式的参数替换为同一个参数。

    /// <summary>
    /// 替换表达式参数
    /// </summary>
    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<Func<Report, bool>> left,Expression<Func<Report, bool>> 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<Func<Report, bool>>(expression , left.Parameters);
    //编译表达式
    var whereFilter = whereExpression.Compile();
    //使用
    DbContext.Report.Where(whereFilter);
}

正想给小老弟显摆一下的时候,他又去写其他骚代码了

骚不过骚不过,完善一下列子,下面是完整的代码

小嫩手不想动的小伙伴可以直接nuget上查找DynamicExpression.Core,直接使用

更多源码看本人github

    /// <summary>
    /// 替换表达式参数
    /// </summary>
    public class ReplaceExpressionVisitor : ExpressionVisitor
    {
        private Dictionary<Expression, Expression> _parameters;

        public ReplaceExpressionVisitor(Dictionary<Expression,Expression> parameters)
        {
            _parameters = parameters;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (_parameters.TryGetValue(node, out Expression _newValue))
            {
                return _newValue;
            }
            return base.Visit(node);
        }
    }
    /// <summary>
    /// 表达式扩展
    /// </summary>
    public static class ExpressionExtension
    {

        /// <summary>
        /// 使用AndAlso合并表达式
        /// </summary>
        /// <param name="exprs"></param>
        /// <returns></returns>
        public static Expression<T> AndAlso<T>(this IList<Expression<T>> 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 < exprs.Count; i++)
            {
                var expr = exprs[i];
                var visitor = GetReplaceExpressionVisitor(expr.Parameters, leftExpr.Parameters);
                var right = visitor.Visit(expr.Body);
                left = Expression.AndAlso(left, right);
            }
            return Expression.Lambda<T>(left, leftExpr.Parameters);
        }

        /// <summary>
        /// 使用AndAlso合并表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns>left AndAlso right</returns>
        public static Expression<T> AndAlso<T>(this Expression<T> left, Expression<T> right)
        {
            return AndAlso(new List<Expression<T>>() { left, right });
        }

        /// <summary>
        /// 使用OrElse合并表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="exprs"></param>
        /// <returns></returns>
        public static Expression<T> OrElse<T>(this IList<Expression<T>> 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 < exprs.Count; i++)
            {
                var expr = exprs[i];
                var visitor = GetReplaceExpressionVisitor(expr.Parameters, leftExpr.Parameters);
                var right = visitor.Visit(expr.Body);
                left = Expression.OrElse(left, right);
            }
            return Expression.Lambda<T>(left, leftExpr.Parameters);
        }

        /// <summary>
        /// 使用OrElse合并表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns>left OrElse right</returns>
        public static Expression<T> OrElse<T>(this Expression<T> left, Expression<T> right)
        {
            return OrElse(new List<Expression<T>>() { left, right });
        }


        /// <summary>
        /// 构建visitor
        /// </summary>
        /// <param name="oldParameters"></param>
        /// <param name="newParameters"></param>
        /// <returns></returns>
        private static ReplaceExpressionVisitor GetReplaceExpressionVisitor(ReadOnlyCollection<ParameterExpression> oldParameters, ReadOnlyCollection<ParameterExpression> newParameters)
        {
            Dictionary<Expression, Expression> dic = new Dictionary<Expression, Expression>();
            for (int i = 0; i < oldParameters.Count; i++)
            {
                dic.Add(oldParameters[i],newParameters[i]);
            }
            return new ReplaceExpressionVisitor(dic);
        }
    }

使用

string connectString = "Data Source=.;Initial Catalog=RportTest;Integrated Security=True";
var optionsBuilder = new DbContextOptionsBuilder<TestContext>();
optionsBuilder.UseSqlServer(connectString);
using (TestContext ctx = new TestContext(optionsBuilder.Options))
{

    Expression<Func<ReportData, bool>> epxr1 = report => report.ID == 2023;
    Expression<Func<ReportData, bool>> epxr2 = report => report.Name == "test1";

    var epxr3 = new List<Expression<Func<ReportData, bool>>>() { 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();
}
  分类: c#Linq.net coreexpression

标签:Core,right,DynamicExpression,exprs,var,Expression,表达式,left
From: https://www.cnblogs.com/Leo_wl/p/17135110.html

相关文章

  • 【ASP.NET Core】标记帮助器——元素筛选
    前一篇中老周从标记帮助的底层介绍关键性的接口,如ITagHelper,它是一个标志,用于识别哪些类属于TagHelper。标记帮助器毕竟是针对HTML标记的,所以得筛选。说白了就是我......
  • [kubernetes]集群中部署CoreDNS服务
    前言从k8s1.11版本开始,k8s集群的dns服务由CoreDNS提供。之前已经使用二进制文件部署了一个三master三node的k8s集群,现在需要在集群内部部署DNS服务。环境信息IP......
  • 版本不兼容(NoSuchMethodError: com.baomidou.mybatisplus.core.toolkit.StringUtils.i
    "C:\ProgramFiles\Java\jdk1.8.0_221\bin\java.exe"-XX:TieredStopAtLevel=1-noverify-Dspring.output.ansi.enabled=always-Dcom.sun.management.jmxremote-Dsprin......
  • Net6 Core Api(.net6)发布到IIS注意事项及显示HTTP 错误500.19解决方法
    Net6CoreApi发布到IIS不同于webapi,依赖框架不同,配置也移至项目内Program.cs一、发布到指定文件夹,和IIS,不过注意IIS应用程序池选择的是“无托管代码“ 访问接口路径......
  • 【ASP.NET Core】标记帮助器——抽象层
    标记帮助器,即TagHelpers。这个嘛,就直接翻译了,叫“标记帮助器”,虽然不好听,但只能这样了。当然你翻译为“标记增强器”也行。所谓标记帮助器,就是针对HTML标签(不管是标准......
  • 将 .net core 程序部署到 docker
    发布项目在VS里面选择你需要发布的程序右击添加docke支持,选择要发布的OS目标是Linux。然后会生成一个Dockerfile文件编辑Dockerfile文件注释掉一部分,然后添加......
  • ASP.NET Core - 自定义中间件
    上一章讲了请求管道与中间件的基本概念和工作模式,也介绍了ASP.NETCore中内置的中间件,这一章介绍一下如何自定义中间件,这是很常用也很重要的内容,日常工作中很多场景我们......
  • 3 .NET Core笔试题
    1.什么是ASP.NETCore?2.ASP.NETCore中AOP的支持有哪些?3.ASP.NETCoreFilter的注册方式有哪些?4.ASP.NETCoreFilter如何支持依赖注入?5.ASP.NETCore如何和读取配......
  • 2 .NET Core笔试题
    1.说说在Linux系统部署ASP.NETCore项目的步骤。2.说说热重载是什么。3.如何理解鉴权和授权两个词4.说说.NET7包含了几大方向的开发?5.如何理解云原生?6.ASP.NETCore应......
  • .NetCore6程序部署到Docker上
    使用Docker部署应用程序首先确保已经安装Docker 桌面软件,如下图: 然后,把需要部署到Docker上面的项目,咱们先添加Docker的支持,启动项目右键 -> 添加 -> Docker支持,选......