.net core 表达式树

本文通过Expression类 实现将 ((User.Name like "%你好%"&&User.Age>10) || User.Name=="admin") 这样的字符串条件转换为 Lambda 表达式,从而达到动态查询的目的。 从此以后,再也不用写那么多参数的查询接口了。

1,什么是表达式树

文档地址:表达式树 (C#) System.Linq.Expressions 命名空间

表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等。
你可以对表达式树中的代码进行编辑和运算。 这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。 有关 LINQ中表达式树的详细信息,请参阅如何使用表达式树生成动态查询 (C#)。
表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL)。 有关 DLR的详细信息,请参阅动态语言运行时概述。
你可以基于匿名 lambda 表达式通过 C# 或者 Visual Basic编译器创建表达式树,或者通过 System.Linq.Expressions 名称空间手动创建。 -官网解释

重点是 x < y 这个里边,实际上是有三个表达式的,把一系列的表达式串起来以树形结构表示的即为表达式树。

2,继承关系

在这里插入图片描述

通过文档可以看出来,要创建表达式树需要通过Expression类,我们的目标是是把输入的字符串转换为一个可以被orm框架识别的lamba表达式,那第一个要知道的就一定是他们之间的关系,
在这里插入图片描述

随便找个Where转到定义可以看到下图的定义
在这里插入图片描述
继承自LambdaExpression
在这里插入图片描述

LambdaExpression继承自Expression
LambdaExpression从官网看只有一个派生类就是where条件中用的类型 Expression<TDelegate>

Expression的派生类 Expression 类 (System.Linq.Expressions) 通过文档可以看到有好多
在这里插入图片描述
Expression的派生类
每个类型的使用方式在相关文档中都能找到。

我们的目标是把字符串转换为一个可以被where方法接受的对象,所以重点就放到lamdaExpress以及Expression的方法上,说实话,要把它完全搞明白,好好阅读文档是必须的。

3,创建一个最简单的表达式树

假定我们的表达式是这样的

Expression<Func<string, bool>> res= x=>x.Length>0;

Func不了解的可以看 FreeTimeWorker:C# 委托->Action<T>->Func<T>,扩展方法

如上,这是个非常简单lambda表达式,从形式上看 它是由参数和方法体组成的

首先创建一个参数表达式 表示上边代码中=>前的x

ParameterExpression parameterExpression = Expression.Parameter(typeof(string),"x");

紧接着创建 x.Lengthx.Length表示的是输入参数的Length属性 这时候需要一个属性表达式

PropertyInfo length = typeof(string).GetProperty("Length");//string的Length属性
MemberExpression left = Expression.Property(parameterExpression, length);//表示输入参数x的Length属性表达式

然后创建右边的表达式,右边是个常量0,这时候需要一个常量表达式

 Expression right = Expression.Constant(0);

最后,他们两的是通过比较做的计算,前边的表达式大于后边的表达式。

Expression result = Expression.GreaterThan(left, right);

执行调用:

res1.Compile().DynamicInvoke("");//false
res1.Compile().DynamicInvoke("1");//true

4,分析字符结构,创建表达式树

User.Name=="admin" || (User.Name like "%你好%"&&User.Age>10)

这是我们的原始字符串,首先需要保证 User是类名,Name,Age是属性名,我们需要做的是拿到每一段的表达式User.Name like "%你好%" User.Age>10 User.Name=="admin" 根据括号关系对表达式做连接,这个示例里,User.Name like "%你好%" User.Age>10 它两是and关系,结果与 User.Name=="admin"是或的关系。

5,实现

   /// <summary>
    ///  根据字符串构建条件支持的比较符 >,<,>= ,<=,==, like  like两种模式,contain,startwith. %x% x%;
    /// </summary>
    /// <typeparam name="TDelegate"></typeparam>
    /// <param name="source"></param>
    /// <param name="condition"> condition中的条件格式为 类名.属性名</param>
    /// <returns></returns>
    public static Expression<TDelegate> BuildCondition<TDelegate>(this Expression<TDelegate> source, string condition)
    {
        try
        {
            if (string.IsNullOrEmpty(condition))
            {
                return source;
            }
            string specchar = "&|()";
            string tempconditon = "";
            int i = 0;
            Stack<string> stack = new Stack<string>();//条件符号,和(
            Stack<Expression> expressions = new Stack<Expression>();
            while (i < condition.Length)
            {
                if (!specchar.Contains(condition[i]))
                {
                    tempconditon += condition[i];
                }
                else
                {
                    switch (condition[i])
                    {
                        case '&':
                            i++;
                            stack.Push("&&");
                            if (!string.IsNullOrEmpty(tempconditon))
                            {
                                expressions.Push(BuildExpression(source, tempconditon));
                            }
                            tempconditon = "";
                            break;
                        case '|':
                            i++;
                            stack.Push("||");
                            if (!string.IsNullOrEmpty(tempconditon))
                            {
                                expressions.Push(BuildExpression(source, tempconditon));
                            }
                            tempconditon = "";
                            break;
                        case '(':
                            stack.Push("(");
                            tempconditon = "";
                            break;
                        case ')':
                            string top = stack.Pop();
                            while (top != "(")
                            {
                                if (!string.IsNullOrEmpty(tempconditon))
                                {
                                    expressions.Push(BuildExpression(source, tempconditon));
                                    tempconditon = "";
                                }
                                if (expressions.Count > 1)
                                {
                                    string smby = top;//不是括号肯定是符号,不是&&就是||
                                    var exp1 = expressions.Pop();
                                    var exp2 = expressions.Pop();
                                    switch (smby)
                                    {
                                        case "&&":
                                            expressions.Push(Expression.And(exp1, exp2));
                                            break;
                                        case "||":
                                            expressions.Push(Expression.Or(exp1, exp2));
                                            break;
                                    }
                                }
                                top = stack.Pop();
                            }
                            if (!string.IsNullOrEmpty(tempconditon))
                            {
                                expressions.Push(BuildExpression(source, tempconditon));
                                tempconditon = "";
                            }
                            break;
                    }
                }
                i++;
            }
            if (!string.IsNullOrWhiteSpace(tempconditon))
            {
                expressions.Push(BuildExpression(source, tempconditon));
            }
            while (expressions.Count > 1)
            {
                var exp1 = expressions.Pop();
                var exp2 = expressions.Pop();
                string smby = stack.Pop();
                switch (smby)
                {
                    case "&&":
                        expressions.Push(Expression.And(exp1, exp2));
                        break;
                    case "||":
                        expressions.Push(Expression.Or(exp1, exp2));
                        break;
                }
            }
            return Expression.Lambda<TDelegate>(expressions.Pop(), source.Parameters.ToArray());
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }
    public static Expression BuildExpression<TDelegate>(Expression<TDelegate> source, string condition)
    {
        Regex r = new Regex("([a-zA-Z][a-zA-Z0-9_]*\\.*[a-zA-Z0-9_]*\\s*)([><=]=*|like)(.+)");
        var res = r.Match(condition);
        if (res.Groups.Count < 4)
        {
            throw new Exception("条件错误");
        }
        string left = res.Groups[1].ToString().Trim();
        string symbol = res.Groups[2].ToString().Trim();
        string right = res.Groups[3].ToString().Trim();
        //根据左边表达式找到propertyinfo

        string[] leftgroup = left.Split('.');
        if (leftgroup.Length < 2)
        {
            throw new Exception("表达式错误");
        }
        var parmExp = source.Parameters.ToList().FirstOrDefault(o => o.Type.Name == leftgroup[0].Trim());
        if (parmExp == null)
        {
            throw new Exception("表达式错误,请确保表达式格式为 TableName.FeildName >= 0");
        }
        var propertyInfo = parmExp.Type.GetProperty(leftgroup[1].Trim());
        MemberExpression leftexp = Expression.Property(parmExp, propertyInfo);
        if (symbol.Trim().ToLower() == "like")
        {
            right = right.Trim().TrimStart('\'').TrimEnd('\'').TrimStart('"').TrimEnd('"').Trim();
            string methodName = "StartsWith";
            if (right.StartsWith("%") && right.EndsWith("%"))
            {
                methodName = "Contains";
            }
            MethodInfo methodInfo = typeof(string).GetMethod(methodName, new Type[] { typeof(string) });
            ConstantExpression c = Expression.Constant(right.TrimStart('%').TrimEnd('%').Trim(), typeof(string));
            return Expression.Call(leftexp, methodInfo, c);
        }
        else
        {
            object rightValue = null;
            if (propertyInfo.PropertyType == typeof(bool))
            {
                if (right == "0")
                {
                    rightValue = false;
                }
                else
                {
                    rightValue = true;
                }
            }
            else if (propertyInfo.PropertyType.BaseType == typeof(System.Enum))
            {
                if (int.TryParse(right, out int rightintvalue))
                {
                    rightValue = System.Enum.Parse(propertyInfo.PropertyType, System.Enum.GetName(propertyInfo.PropertyType, rightintvalue));
                }
                else
                {
                    rightValue = System.Enum.Parse(propertyInfo.PropertyType, right);
                }
            }
            else if (propertyInfo.PropertyType == typeof(int))
            {
                if (int.TryParse(right, out int rightintvalue))
                {
                    rightValue = rightintvalue;
                }
                else
                {
                    throw new Exception($"表达式错误类型转换失败{propertyInfo.Name}");
                }
            } 
            else //string类型的 ==
            {
                rightValue = right;
            }
            ConstantExpression rightexp = Expression.Constant(rightValue, propertyInfo.PropertyType);
            switch (symbol)
            {
                case ">":
                    return Expression.GreaterThan(leftexp, rightexp);
                case "<":
                    return Expression.LessThan(leftexp, rightexp);
                case ">=":
                    return Expression.GreaterThanOrEqual(leftexp, rightexp);
                case "==":
                    return Expression.Equal(leftexp, rightexp);
                case "<=":
                    return Expression.LessThanOrEqual(leftexp, rightexp);
                default:
                    throw new Exception($"表达式错误,未定义符号{symbol}");
            }
        }
    }

6,使用示例

Expression<Func<ViewTestModel, bool>> condition = (item) => true;
string str = @$"(ViewTestModel.Range==1||ViewTestModel.Range>7)&&ViewTestModel.Tel like '%135%' && ViewTestModel.Id>0";
condition= condition.BuildCondition(str);
return _sqlSugarClient.Queryable<TestModel>().Select<ViewTestModel>().Where(condition).ToPage(_httpParameter);
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: .NET Core 中使用正则表达式过滤字符串中的 null 字符 (\0) 可以使用如下代码: ``` string input = "This is a string\0with null characters."; string pattern = @"\0"; string replacement = ""; Regex rgx = new Regex(pattern); string result = rgx.Replace(input, replacement); ``` 在这里,`input` 变量包含一个带有 null 字符的字符串。我们使用正则表达式 `@"\0"` 来匹配 null 字符。然后使用 `Regex` 类的 `Replace` 方法将匹配的 null 字符替换为空字符串。最终,结果字符串 `result` 将包含原始字符串中的所有字符,但不包含任何 null 字符。 ### 回答2: .NET Core中的正则表达式过滤\0可以使用反向引用来实现。在正则表达式中,\0代表所匹配的整个表达式,因此要过滤掉\0,可以使用反向引用将匹配的部分替换为空字符串。 以下是一个示例代码: ```csharp using System; using System.Text.RegularExpressions; public class Program { public static void Main() { // 要过滤的字符串 string input = "Hello, \0 World!"; // 创建正则表达式对象 Regex regex = new Regex(@"\0"); // 使用空字符串替换匹配的部分 string output = regex.Replace(input, string.Empty); // 输出过滤后的结果 Console.WriteLine(output); } } ``` 在上述代码中,我们创建了一个正则表达式对象`regex`,并使用`\0`匹配要过滤的字符。然后使用`Replace`方法将匹配的部分替换为空字符串,得到过滤后的结果。 运行上述代码将输出`Hello, World!`,即成功过滤掉了`\0`字符。 使用反向引用可以帮助我们在正则表达式中实现各种过滤和替换的需求,如过滤掉其他特殊字符、过滤掉HTML标签等。 ### 回答3: .NET Core 正则表达式可以通过一些特殊字符来过滤掉`\0`(空字符)。 在正则表达式中,`\0`表示一个空字符,也可以用`\x00`表示。可以使用正则表达式中的特殊字符`[^]`来匹配除了空字符以外的任意字符。例如,可以使用`[^^\x00]`来匹配除了空字符以外的所有字符。 下面是一个示例代码,演示了如何使用正则表达式过滤掉`\0`: ```csharp using System; using System.Text.RegularExpressions; class Program { static void Main() { string input = "Hello\0World"; // 过滤掉空字符 string filtered = Regex.Replace(input, "[^\x00]", ""); Console.WriteLine(filtered); // 输出:HelloWorld } } ``` 在上面的代码中,我们使用`Regex.Replace`方法来替换掉匹配的字符。`[^\x00]`表示匹配除了空字符以外的任意字符。然后我们将匹配到的字符替换为空字符串,从而达到过滤的效果。 需要注意的是,在使用正则表达式过滤字符时,需要先将字符串转换为正确的编码。如果不同编码可能会导致不同的字符表示方式,这可能影响到正则表达式的匹配结果。 希望这个回答能够帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值