C# Expression详解(高级)
LINQ在本地查询IEnumerbale主要是用委托来作为传参,而解析型查询IQueryable则用Expression来作为传参:
public static IEnumerable<T> Where<T>(this IEnumerable<T> enumable, Func<T, bool> func)
public static IQueryable<T> Where<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> func)
一、Expression是什么
1、如何定义
Expression<Func<TSource, bool>>就是表达式目录树Expression不能带有大括号,只能有一行代码
2、和委托的区别
在委托外面包裹一层
Expression<>就是表达式目录树表达式目录树可以通过
Compile()转换成一个委托
3、Expression本质
- 表达式目录树是一个类的封装,描述了一个结构,有身体部分和参数部分
 - 身体部分分为左边和右边,内部描述了左边和右边之间的关系,可以不断的往下拆分,类似于二叉树
 - 表达式目录树展开后的每一个节点也是一个表达式目录树
 
Expression<Func<People, bool>> expression = p => p.Id == 10;
Func<People, bool> func = expression.Compile();
bool bResult = func.Invoke(new People()
{
    Id = 10,
    Name = "张三"
});
二、Expression与Expression Tree
首先我们来写下一些代码:
Expression<Func<int, int>> expression = (num) => num + 5;
Console.WriteLine($"NodeType:{expression.NodeType}");
Console.WriteLine($"Body:{expression.Body}");
Console.WriteLine($"Body Type: {expression.Body.GetType()}");
Console.WriteLine($"Body NodeType: {expression.Body.NodeType}");
输出如下:
NodeType:Lambda
Body:(num + 5)
Body Type: System.Linq.Expressions.SimpleBinaryExpression
Body NodeType: Add
我们将expression转为LambdaExpression看看都有啥:
if (expression.NodeType == ExpressionType.Lambda)
{
    var lambda = (LambdaExpression)expression;
    var parameter = lambda.Parameters.Single();
    Console.WriteLine($"parameter.Name:{parameter.Name}");
    Console.WriteLine($"parameter.Type:{parameter.Type}");
    Console.WriteLine($"parameter.ReturnType:{lambda.ReturnType}");
}
输出如下:
parameter.Name:num
parameter.Type:System.Int32
parameter.ReturnType:System.Int32
由于我们知道expression.Body是BinaryExpression,那么我们就将其转为它,然后我们继续看下去:
if (expression.Body.NodeType == ExpressionType.Add)
{
    var binaryExpreesion = (BinaryExpression)expression.Body;
    Console.WriteLine($"Left Type:{binaryExpreesion.Left.GetType()}");
    Console.WriteLine($"Left NodeType:{binaryExpreesion.Left.NodeType}");
    Console.WriteLine($"Right Type:{binaryExpreesion.Right.GetType()}");
    Console.WriteLine($"Right NodeType:{binaryExpreesion.Right.NodeType}");
    if (binaryExpreesion.Left is ParameterExpression parameterExpreesion)
    {
        Console.WriteLine($"parameterExpreesion.Name:{parameterExpreesion.Name}");
        Console.WriteLine($"parameterExpreesion.Type:{parameterExpreesion.Type}");
    }
    if (binaryExpreesion.Right is ConstantExpression constantExpreesion)
    {
        Console.WriteLine($"constantExpreesion.Value:{constantExpreesion.Value}" );
    }
}
输出如下:
Left Type:System.Linq.Expressions.PrimitiveParameterExpression`1[System.Int32]
Left NodeType:Parameter
Right Type:System.Linq.Expressions.ConstantExpression
Right NodeType:Constant
parameterExpreesion.Name:num
parameterExpreesion.Type:System.Int32
constantExpreesion.Value:5
最后我们将表达式树转为委托:
var @delegate = expression.Compile();
Console.WriteLine(@delegate?.Invoke(2));
输出:
7 //2+5
实际上,通过Expression<Func<int, int>> expression = (num) => num + 5;,赋值后的expression变成了一个表达式树,它的结构是这样的:

而有意思的是二元表达式树BinaryExpression是一个二叉树,而LambdaExpression则是一个支持参数的表达式,能够通过其Parameters属性知道传入的参数的类型和数量,通过ReturnType知道返回值是什么类型
而我们再看看整个关于Expression的继承关系链:

因此,我们也可以显式的通过各自Expreesion的实现子类来创建跟lambda表达式一样的结果:
var parameterExpreesion1 = Expression.Parameter(typeof(int), "num");
BinaryExpression binaryExpression1 = Expression.MakeBinary(ExpressionType.Add, parameterExpreesion1, Expression.Constant(5));
Expression<Func<int, int>> expression1 = Expression.Lambda<Func<int, int>>(binaryExpression1, parameterExpreesion1);
if (expression1.Body.NodeType == ExpressionType.Add)
{
    var binaryExpreesion1 = (BinaryExpression)expression1.Body;
    Console.WriteLine($"Left Type:{binaryExpreesion1.Left.GetType()}");
    Console.WriteLine($"Left NodeType:{binaryExpreesion1.Left.NodeType}");
    Console.WriteLine($"Right Type:{binaryExpreesion1.Right.GetType()}");
    Console.WriteLine($"Right NodeType:{binaryExpreesion1.Right.NodeType}");
    if (binaryExpreesion1.Left is ParameterExpression parameterExpreesion2)
    {
        Console.WriteLine($"parameterExpreesion.Name:{parameterExpreesion2.Name}");
        Console.WriteLine($"parameterExpreesion.Type:{parameterExpreesion2.Type}");
    }
    if (binaryExpreesion1.Right is ConstantExpression constantExpreesion1)
    {
        Console.WriteLine($"constantExpreesion.Value:{constantExpreesion1.Value}");
    }
    var @delegate1 = expression1.Compile();
    Console.WriteLine(@delegate1(2));
输出结果:
Left Type:System.Linq.Expressions.PrimitiveParameterExpression`1[System.Int32]
Left NodeType:Parameter
Right Type:System.Linq.Expressions.ConstantExpression
Right NodeType:Constant
parameterExpreesion.Name:num
parameterExpreesion.Type:System.Int32
constantExpreesion.Value:5
result:7
我们则发现,结果是一模一样的,但是费劲了很多,因此用lamda构建表达式树是一个非常愉快的语法糖,让你能够愉快的在使用表达式和表达式树
三、Expression动态拼装
1、最基础版本
Expression<Func<int>> expression = () => 123 + 234;
//常量表达式
ConstantExpression expression1 = Expression.Constant(123);
ConstantExpression expression2 = Expression.Constant(234);
//二元表达式
BinaryExpression binaryExpression = Expression.Add(expression1, expression2);
Expression<Func<int>> expressionReslut = Expression.Lambda<Func<int>>(binaryExpression);
Func<int> func = expressionReslut.Compile();
int iResult = func.Invoke();
2、带参数版本
Expression<Func<int, int>> expression1 = m => m + 1;
Func<int, int> func = expression1.Compile();
int iResult = func.Invoke(5);
//参数表达式
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "m");
//常量表达式
ConstantExpression constant = Expression.Constant(1, typeof(int));
//二元表达式
BinaryExpression addExpression = Expression.Add(parameterExpression, constant);
Expression<Func<int, int>> expression = Expression.Lambda<Func<int, int>>(addExpression, new ParameterExpression[1]
{
    parameterExpression
});
Func<int, int> func1 = expression.Compile();
int iResult1 = func1.Invoke(5);
3、带有多个参数
Expression<Func<int, int, int>> expression = (m, n) => m * n + 2;
Func<int, int, int> func = expression.Compile();
int iResult = func.Invoke(10, 20);
//参数表达式
ParameterExpression parameterExpressionM = Expression.Parameter(typeof(int), "m");
ParameterExpression parameterExpressionN = Expression.Parameter(typeof(int), "n");
//二元表达式
BinaryExpression multiply = Expression.Multiply(parameterExpressionM, parameterExpressionN);
//常量表达式
ConstantExpression constantExpression = Expression.Constant(2);
//二元表达式
BinaryExpression plus = Expression.Add(multiply, constantExpression);
Expression<Func<int, int, int>> expression1 = Expression.Lambda<Func<int, int, int>>(plus, new ParameterExpression[2]
{
    parameterExpressionM,
    parameterExpressionN
});
Func<int, int, int> func1 = expression1.Compile();
int iResult1 = func1.Invoke(10, 20);
4、对象字段值比较
类似于这种比较复杂的,建议大家可以反编译看看
Expression<Func<People, bool>> predicate = c => c.Id == 10;
Func<People, bool> func = predicate.Compile();
bool bResult = func.Invoke(new People()
{
    Id = 10
});
//参数表达式
ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "c");
//反射获取属性
FieldInfo fieldId = typeof(People).GetField("Id");
//通过parameterExpression来获取调用Id
MemberExpression idExp = Expression.Field(parameterExpression, fieldId);
//常量表达式
ConstantExpression constant10 = Expression.Constant(10, typeof(int));
//二元表达式
BinaryExpression expressionExp = Expression.Equal(idExp, constant10);
Expression<Func<People, bool>> predicate1 = Expression.Lambda<Func<People, bool>>(expressionExp, new ParameterExpression[1]
{
    parameterExpression
});
Func<People, bool> func1 = predicate1.Compile();
bool bResult1 = func1.Invoke(new People()
{
    Id = 10
});
5、多条件
如果遇到很长的表达式目录树,拼装建议从右往左拼装
Expression<Func<People, bool>> predicate = c => c.Id.ToString() == "10" && c.Name.Equals("张三") && c.Age > 35;
Func<People, bool> func = predicate.Compile();
bool bResult = func.Invoke(new People()
{
    Id = 10,
    Name = "张三",
    Age = 36
});
ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "c");
//c.Age > 35
ConstantExpression constant35 = Expression.Constant(35);
PropertyInfo propAge = typeof(People).GetProperty("Age");
MemberExpression ageExp = Expression.Property(parameterExpression, propAge);
BinaryExpression cagExp = Expression.GreaterThan(ageExp, constant35);
//c.Name.Equals("张三")
ConstantExpression constantrichard = Expression.Constant("张三");
PropertyInfo propName = typeof(People).GetProperty("Name");
MemberExpression nameExp = Expression.Property(parameterExpression, propName);
MethodInfo equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
MethodCallExpression NameExp = Expression.Call(nameExp, equals, constantrichard);
//c.Id.ToString() == "10"
ConstantExpression constantExpression10 = Expression.Constant("10", typeof(string));
FieldInfo fieldId = typeof(People).GetField("Id");
var idExp = Expression.Field(parameterExpression, fieldId);
MethodInfo toString = typeof(int).GetMethod("ToString", new Type[0]);
var toStringExp = Expression.Call(idExp, toString, Array.Empty<Expression>());
var EqualExp = Expression.Equal(toStringExp, constantExpression10);
//c.Id.ToString() == "10"&& c.Name.Equals("张三")&& c.Age > 35
var plus = Expression.AndAlso(EqualExp, NameExp);
var exp = Expression.AndAlso(plus, cagExp);
Expression<Func<People, bool>> predicate1 = Expression.Lambda<Func<People, bool>>(exp, new ParameterExpression[1]
{
    parameterExpression
});
Func<People, bool> func1 = predicate1.Compile();
bool bResult1 = func1.Invoke(new People()
{
    Id = 10,
    Name = "张三",
    Age = 36
});
四、Expression应用之Mapper映射
需求:需要把People字段值映射到PeopleCopy字段
1、硬编码
性能好,不灵活;不能共用
PeopleCopy peopleCopy0 = new PeopleCopy()
{
    Id = people.Id,
    Name = people.Name,
    Age = people.Age
};
2、反射
灵活,但是性能不好
using System;
namespace MyExpression.MappingExtend
{
    public class ReflectionMapper
    {
        /// <summary>
        /// 反射
        /// </summary>
        /// <typeparam name="TIn"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="tIn"></param>
        /// <returns></returns>
        public static TOut Trans<TIn, TOut>(TIn tIn)
        {
            TOut tOut = Activator.CreateInstance<TOut>();
            foreach (var itemOut in tOut.GetType().GetProperties())
            {
                var propIn = tIn.GetType().GetProperty(itemOut.Name);
                itemOut.SetValue(tOut, propIn.GetValue(tIn));
            }
            foreach (var itemOut in tOut.GetType().GetFields())
            {
                var fieldIn = tIn.GetType().GetField(itemOut.Name);
                itemOut.SetValue(tOut, fieldIn.GetValue(tIn));
            }
            return tOut;
        }
    }
}
调用
PeopleCopy peopleCopy1 = ReflectionMapper.Trans<People, PeopleCopy>(people);
3、序列化
灵活,但是性能不好
using Newtonsoft.Json;
namespace MyExpression.MappingExtend
{
    public class SerializeMapper
    {
        /// <summary>
        /// 序列化
        /// </summary>
        /// <typeparam name="TIn"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        public static TOut Trans<TIn, TOut>(TIn tIn)
        {
            string strJson = JsonConvert.SerializeObject(tIn);
            return JsonConvert.DeserializeObject<TOut>(strJson);
        }
    }
}
调用
PeopleCopy peopleCopy2 = SerializeMapper.Trans<People, PeopleCopy>(people);
4、Expression动态拼接+普通缓存
- 把
People变成PeopleCopy的过程封装在一个委托中,这个委托通过表达式目录树Compile出来,过程动态拼装适应不同的类型 - 第一次生成的时候,保存一个委托在缓存中,如果第二次来,委托就可以直接从缓存中获取到,直接运行委托,效率高
 
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace MyExpression.MappingExtend
{
    public class ExpressionMapper
    {
        /// <summary>
        /// 字典缓存,保存的是委托,委托内部是转换的动作
        /// </summary>
        private static Dictionary<string, object> _Dic = new Dictionary<string, object>();
        /// <summary>
        /// Expression动态拼接+普通缓存
        /// </summary>
        /// <typeparam name="TIn"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="tIn"></param>
        /// <returns></returns>
        public static TOut Trans<TIn, TOut>(TIn tIn)
        {
            string key = $"funckey_{typeof(TIn).FullName}_{typeof(TOut).FullName}";
            if (!_Dic.ContainsKey(key))
            {
                ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
                List<MemberBinding> memberBindingList = new List<MemberBinding>();
                foreach (var item in typeof(TOut).GetProperties())
                {
                    MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                    MemberBinding memberBinding = Expression.Bind(item, property);
                    memberBindingList.Add(memberBinding);
                }
                foreach (var item in typeof(TOut).GetFields())
                {
                    MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
                    MemberBinding memberBinding = Expression.Bind(item, property);
                    memberBindingList.Add(memberBinding);
                }
                MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
                Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
                {
                    parameterExpression
                });
                Func<TIn, TOut> func = lambda.Compile();//拼装是一次性的
                _Dic[key] = func;
            }
            return ((Func<TIn, TOut>)_Dic[key]).Invoke(tIn);
        }
    }
}
调用
PeopleCopy peopleCopy3 = ExpressionMapper.Trans<People, PeopleCopy>(people);
5、Expression动态拼接+泛型缓存
泛型缓存,就是为每一组类型的组合,生成一个副本,性能最高
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace MyExpression.MappingExtend
{
    /// <summary>
    /// Expression动态拼接+泛型缓存
    /// </summary>
    /// <typeparam name="TIn"></typeparam>
    /// <typeparam name="TOut"></typeparam>
    public class ExpressionGenericMapper<TIn, TOut>//Mapper`2
    {
        private static Func<TIn, TOut> _FUNC = null;
        static ExpressionGenericMapper()
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
            List<MemberBinding> memberBindingList = new List<MemberBinding>();
            foreach (var item in typeof(TOut).GetProperties())
            {
                MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            foreach (var item in typeof(TOut).GetFields())
            {
                MemberExpression property = Expression.Field(parameterExpression, typeof(TIn).GetField(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }
            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
            Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[]
            {
                    parameterExpression
            });
            _FUNC = lambda.Compile();//拼装是一次性的
        }
        public static TOut Trans(TIn t)
        {
            return _FUNC(t);
        }
    }
}
调用
PeopleCopy peopleCopy4 = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);
6、性能比较
Expression动态拼接+泛型缓存性能高,而且灵活
long common = 0;
long generic = 0;
long cache = 0;
long reflection = 0;
long serialize = 0;
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for (int i = 0; i < 1_000_000; i++)
    {
        PeopleCopy peopleCopy = new PeopleCopy()
        {
            Id = people.Id,
            Name = people.Name,
            Age = people.Age
        };
    }
    watch.Stop();
    common = watch.ElapsedMilliseconds;
}
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for (int i = 0; i < 1_000_000; i++)
    {
        PeopleCopy peopleCopy = ReflectionMapper.Trans<People, PeopleCopy>(people);
    }
    watch.Stop();
    reflection = watch.ElapsedMilliseconds;
}
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for (int i = 0; i < 1_000_000; i++)
    {
        PeopleCopy peopleCopy = SerializeMapper.Trans<People, PeopleCopy>(people);
    }
    watch.Stop();
    serialize = watch.ElapsedMilliseconds;
}
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for (int i = 0; i < 1_000_000; i++)
    {
        PeopleCopy peopleCopy = ExpressionMapper.Trans<People, PeopleCopy>(people);
    }
    watch.Stop();
    cache = watch.ElapsedMilliseconds;
}
{
    Stopwatch watch = new Stopwatch();
    watch.Start();
    for (int i = 0; i < 1_000_000; i++)
    {
        PeopleCopy peopleCopy = ExpressionGenericMapper<People, PeopleCopy>.Trans(people);
    }
    watch.Stop();
    generic = watch.ElapsedMilliseconds;
}
Console.WriteLine($"common = { common} ms");
Console.WriteLine($"reflection = { reflection} ms");
Console.WriteLine($"serialize = { serialize} ms");
Console.WriteLine($"cache = { cache} ms");
Console.WriteLine($"generic = { generic} ms");
运行结果
common = 32 ms
reflection = 1026 ms
serialize = 2510 ms
cache = 236 ms
generic = 31 ms
五、ExpressionVisitor解析Expression
1、Expression解析
Expression是通过访问者模式进行解析的,官方提供了ExpressionVisitor抽象类ExpressionVisitor的Visit方法是解析表达式目录树的一个入口,Visit方法判断Expression是一个什么表达式目录树,走不同的细分方法进行进一步解析ExpressionVisitor的VisitBinary方法是对二元表达式的解析,所有复杂的表达式都会拆解成二元表达式进行解析
2、Expression修改
自定义一个OperationsVisitor,继承自ExpressionVisitor,复写父类的VisitBinary方法,修改Expression的解析
OperationsVisitor定义
using System.Linq.Expressions;
namespace MyExpression
{
    /// <summary>
    /// 自定义Visitor
    /// </summary>
    public class OperationsVisitor : ExpressionVisitor
    {
        /// <summary>
        /// 覆写父类方法;//二元表达式的访问
        /// 把表达式目录树中相加改成相减,相乘改成相除
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        protected override Expression VisitBinary(BinaryExpression b)
        {
            if (b.NodeType == ExpressionType.Add)//相加
            {
                Expression left = this.Visit(b.Left);
                Expression right = this.Visit(b.Right);
                return Expression.Subtract(left, right);//相减
            }
            else if (b.NodeType==ExpressionType.Multiply) //相乘
            {
                Expression left = this.Visit(b.Left);
                Expression right = this.Visit(b.Right);
                return Expression.Divide(left, right); //相除
            }
            return base.VisitBinary(b);
        }
    }
}
Expression解析转换
Expression<Func<int, int, int>> exp = (m, n) => m * n + 2;
Console.WriteLine(exp.ToString());
OperationsVisitor visitor = new OperationsVisitor();
Expression expNew = visitor.Visit(exp);
Console.WriteLine(expNew.ToString());
运行结果
(m, n) => ((m * n) + 2)
(m, n) => ((m / n) - 2)
3、封装多条件连接扩展方法
扩展方法实现
/// <summary>
/// 合并表达式 And Or Not扩展方法
/// </summary>
public static class ExpressionExtend
{
    /// <summary>
    /// 合并表达式 expr1 AND expr2
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="expr1"></param>
    /// <param name="expr2"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        if (expr1 == null || expr2 == null)
        {
            throw new Exception("null不能处理");
        }
        ParameterExpression newParameter = Expression.Parameter(typeof(T), "x");
        NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
        Expression left = visitor.Visit(expr1.Body);
        Expression right = visitor.Visit(expr2.Body);
        BinaryExpression body = Expression.And(left, right);
        return Expression.Lambda<Func<T, bool>>(body, newParameter);
    }
    /// <summary>
    /// 合并表达式 expr1 or expr2
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="expr1"></param>
    /// <param name="expr2"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        if (expr1 == null || expr2 == null)
        {
            throw new Exception("null不能处理");
        }
        ParameterExpression newParameter = Expression.Parameter(typeof(T), "x");
        NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);
        Expression left = visitor.Visit(expr1.Body);
        Expression right = visitor.Visit(expr2.Body);
        BinaryExpression body = Expression.Or(left, right);
        return Expression.Lambda<Func<T, bool>>(body, newParameter);
    }
    /// <summary>
    /// 表达式取非
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="expr"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
    {
        if (expr == null)
        {
            throw new Exception("null不能处理");
        }
        ParameterExpression newParameter = expr.Parameters[0];
        UnaryExpression body = Expression.Not(expr.Body);
        return Expression.Lambda<Func<T, bool>>(body, newParameter);
    }
}
自定义Visitor
internal class NewExpressionVisitor : ExpressionVisitor
{
    public ParameterExpression _NewParameter { get; private set; }
    public NewExpressionVisitor(ParameterExpression param)
    {
        this._NewParameter = param;
    }
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return this._NewParameter;
    }
}
数据过滤方法定义
/// <summary>
/// 筛选数据执行
/// </summary>
/// <param name="func"></param>
private static void Do(Expression<Func<People, bool>> func)
{
    List<People> people = new List<People>()
    {
        new People(){Id=4,Name="123",Age=4},
        new People(){Id=5,Name="234",Age=5},
        new People(){Id=6,Name="345",Age=6},
    };
    List<People> peopleList = people.Where(func.Compile()).ToList();
}
Expression拼接
Expression<Func<People, bool>> lambda1 = x => x.Age > 5;
Expression<Func<People, bool>> lambda2 = x => x.Id > 5;
Expression<Func<People, bool>> lambda3 = lambda1.And(lambda2);//且
Expression<Func<People, bool>> lambda4 = lambda1.Or(lambda2);//或
Expression<Func<People, bool>> lambda5 = lambda1.Not();//非
Do(lambda3);
Do(lambda4);
Do(lambda5);
六、ExpressionVisitor应用之ToSql
需求:实现ORM框架Expression映射成SQL
自定义一个ConditionBuilderVisitor
继承自ExpressionVisitor,复写父类的方法,Expression解析过程中实现SQL的拼接
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace MyExpression
{
    public class ConditionBuilderVisitor : ExpressionVisitor
    {
        private Stack<string> _StringStack = new Stack<string>();
        /// <summary>
        /// 返回拼装好的sql条件表达式
        /// </summary>
        /// <returns></returns>
        public string Condition()
        {
            string condition = string.Concat(this._StringStack.ToArray());
            this._StringStack.Clear();
            return condition;
        }
        /// <summary>
        /// 如果是二元表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node == null) throw new ArgumentNullException("BinaryExpression");
            this._StringStack.Push(")");
            base.Visit(node.Right);//解析右边
            this._StringStack.Push(" " + ToSqlOperator(node.NodeType) + " ");
            base.Visit(node.Left);//解析左边
            this._StringStack.Push("(");
            return node;
        }
        /// <summary>
        /// 解析属性
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node == null) throw new ArgumentNullException("MemberExpression");
            if (node.Expression is ConstantExpression)
            {
                var value1 = this.InvokeValue(node);
                var value2 = this.ReflectionValue(node);
                this._StringStack.Push("'" + value2 + "'");
            }
            else
            {
                this._StringStack.Push(" [" + node.Member.Name + "] ");
            }
            return node;
        }
        private string ToSqlOperator(ExpressionType type)
        {
            switch (type)
            {
                case (ExpressionType.AndAlso):
                case (ExpressionType.And):
                    return "AND";
                case (ExpressionType.OrElse):
                case (ExpressionType.Or):
                    return "OR";
                case (ExpressionType.Not):
                    return "NOT";
                case (ExpressionType.NotEqual):
                    return "<>";
                case ExpressionType.GreaterThan:
                    return ">";
                case ExpressionType.GreaterThanOrEqual:
                    return ">=";
                case ExpressionType.LessThan:
                    return "<";
                case ExpressionType.LessThanOrEqual:
                    return "<=";
                case (ExpressionType.Equal):
                    return "=";
                default:
                    throw new Exception("不支持该方法");
            }
        }
        private object InvokeValue(MemberExpression member)
        {
            var objExp = Expression.Convert(member, typeof(object));//struct需要
            return Expression.Lambda<Func<object>>(objExp).Compile().Invoke();
        }
        private object ReflectionValue(MemberExpression member)
        {
            var obj = (member.Expression as ConstantExpression).Value;
            return (member.Member as FieldInfo).GetValue(obj);
        }
        /// <summary>
        /// 常量表达式
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitConstant(ConstantExpression node)
        {
            if (node == null) throw new ArgumentNullException("ConstantExpression");
            this._StringStack.Push("" + node.Value + "");
            return node;
        }
        /// <summary>
        /// 方法表达式
        /// </summary>
        /// <param name="m"></param>
        /// <returns></returns>
        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m == null) throw new ArgumentNullException("MethodCallExpression");
            string format;
            switch (m.Method.Name)
            {
                case "StartsWith":
                    format = "({0} LIKE '{1}%')";
                    break;
                case "Contains":
                    format = "({0} LIKE '%{1}%')";
                    break;
                case "EndsWith":
                    format = "({0} LIKE '%{1}')";
                    break;
                default:
                    throw new NotSupportedException(m.NodeType + " is not supported!");
            }
            this.Visit(m.Object);
            this.Visit(m.Arguments[0]);
            string right = this._StringStack.Pop();
            string left = this._StringStack.Pop();
            this._StringStack.Push(String.Format(format, left, right));
            return m;
        }
    }
}
ConstantSqlString泛型缓存缓存生成的sql
using System;
using System.Linq;
namespace MyExpression
{
    public class ConstantSqlString<T>
    {
        /// <summary>
        /// 泛型缓存,一个类型一个缓存
        /// </summary>
        private static string FindSql = null;
        /// <summary>
        /// 获取查询sql
        /// </summary>
        static ConstantSqlString()
        {
            Type type = typeof(T);
            FindSql = $"Select {string.Join(',', type.GetProperties().Select(c => $"[{c.Name}]").ToList())} from {type.Name}";
        }
        /// <summary>
        /// 获取查询sql+条件筛选
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public static string GetQuerySql(string exp)
        {
            return $"{FindSql} where {exp}";
        }
    }
}
普通多条件
Expression<Func<People, bool>> lambda = x => x.Age > 5
                                             && x.Id > 5
                                             && x.Name.StartsWith("1") //  like '1%'
                                             && x.Name.EndsWith("1") //  like '%1'
                                             && x.Name.Contains("1");//  like '%1%'
ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
vistor.Visit(lambda);
string sql = ConstantSqlString<People>.GetQuerySql(vistor.Condition());
Console.WriteLine(sql);
外部参数变量
string name = "AAA";
Expression<Func<People, bool>> lambda = x => x.Age > 5 && x.Name == name || x.Id > 5;
ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
vistor.Visit(lambda);
string sql = ConstantSqlString<People>.GetQuerySql(vistor.Condition());
Console.WriteLine(sql);
内部常量多条件
Expression<Func<People, bool>> lambda = x => x.Age > 5 || (x.Name == "A" && x.Id > 5);
ConditionBuilderVisitor vistor = new ConditionBuilderVisitor();
vistor.Visit(lambda);
string sql = ConstantSqlString<People>.GetQuerySql(vistor.Condition());
Console.WriteLine(sql);
运行结果
Select [Age],[Name] from People where ((((( [Age]  > 5) AND ( [Id]  > 5)) AND ( [Name]  LIKE '1%')) AND ( [Name]  LIKE '%1')) AND ( [Name]  LIKE '%1%'))
Select [Age],[Name] from People where ((( [Age]  > 5) AND ( [Name]  = 'AAA')) OR ( [Id]  > 5))
Select [Age],[Name] from People where (( [Age]  > 5) OR (( [Name]  = A) AND ( [Id]  > 5)))
七、多条件拼接示例
1、动态拼接lambda表达式
/// <summary>
/// Lambda表达式拼接扩展类
/// </summary>
public static class Utility
{
    /// <summary>
    /// Lambda表达式拼接
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <param name="merge"></param>
    /// <returns></returns>
    public 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);
        // 用第一个lambda表达式中的参数替换第二个lambda表达式中的参数
        var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
        // 将lambda表达式体的组合应用于第一个表达式中的参数
        return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    }
    /// <summary>
    /// and扩展
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.And);
    }
    /// <summary>
    /// or扩展
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
    {
        return first.Compose(second, Expression.Or);
    }
}
/// <summary>
///
/// </summary>
public class ParameterRebinder : ExpressionVisitor
{
    private readonly Dictionary<ParameterExpression, ParameterExpression> map;
    public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
    {
        this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
    }
    /// <summary>
    ///
    /// </summary>
    /// <param name="map"></param>
    /// <param name="exp"></param>
    /// <returns></returns>
    public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
    {
        return new ParameterRebinder(map).Visit(exp);
    }
    /// <summary>
    ///
    /// </summary>
    /// <param name="p"></param>
    /// <returns></returns>
    protected override Expression VisitParameter(ParameterExpression p)
    {
        ParameterExpression replacement;
        if (map.TryGetValue(p, out replacement))
        {
            p = replacement;
        }
        return base.VisitParameter(p);
    }
}
//常见用途,比如我们数据层封装了如下方法:
/// <summary>
/// 获取一个实体
/// </summary>
/// <param name="expWhere">查询条件</param>
/// <returns></returns>
public virtual T Find(Expression<Func<T, bool>> expWhere) {
    T entity = null;
    using (IDbContext MsSqlDB = CreateSqlContext()) {
        IQuery<T> q = MsSqlDB.Query<T>();
        entity = q.Where(expWhere).FirstOrDefault();
    }
    return entity;
}
//上层调用时候可以动态拼接lambda表达式:
Expression<Func<Books, bool>> where = c => true;
if (bookID == "-1")
{
    where = where.And(a => a.Id == "001");
}
if (bookName == "-1")
{
    where = where.Or(a => a.Name == "Test");
}
new BaseLocalBll<Books>().Find(where);
2、使用Expression进行查询拼接
public static class DynamicLinqExpressions
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.Or(expr1.Body, invokedExpr), expr1.Parameters);
    }
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.And(expr1.Body, invokedExpr), expr1.Parameters);
    }
}
//使用方法
protected void btnSearch_Click(object sender, EventArgs e)
{
    using(LinqDBDataContext db = new LinqDBDataContext())
    {
        var list = db.StuInfo;
        var where = DynamicLinqExpressions.True<StuInfo>();
        if(txtName.Text.Trim().Length!=0)
        {
            where = where.And(p => p.StuName.Contains(txtName.Text.Trim()));
        }
        if(txtAge.Text.Trim().Length!=0)
        {
            where = where.And(p => p.StuAge == Convert.ToInt32(txtAge.Text.Trim()));
        }
        var result = list.Where(where.Compile()).ToList();
        this.repStuInfo.DataSource = result;
        this.repStuInfo.DataBind();
    }
}
3、简单的多条件拼接方法
var list = new List<Expression<Func<Student, bool>>>();
list.Add(o => true);
if (stuID != 0) list.Add(o => o.ID == stuID);
if (sex != "") list.Add(o => o.Sex == sex);
if (age != null) list.Add(o => o.Age > age);
Expression<Func<Student, bool>> expWhere = null;
foreach (var expression in list)
{
    expWhere = expWhere.And(expression);
}
Students.Where(expWhere.Compile()).ToList();
												
											C# Expression详解(高级)的更多相关文章
- hibernate Expression详解
		
关键字: hibernate expression hibernate Expression详解Expression.gt:对应SQL条件中的"field > value " ...
 - ie浏览器css中的行为expression详解
		
CSS中的行为——expression (ie only) 最近对CSS中的行为比较感兴趣,虽然是不符合标准的也只有ie才能识别,但是他确实给css的功能扩展了不少.下面是摘自互联网上的文字和例子,因 ...
 - 【转】Linux netstat命令详解,高级面试必备
		
简介 Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Member ...
 - Python sorted函数详解(高级篇)
		
sorted() 函数对所有可迭代的对象进行排序操作. sort 与 sorted 区别: sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作. list 的 s ...
 - Python中的高级数据结构详解
		
这篇文章主要介绍了Python中的高级数据结构详解,本文讲解了Collection.Array.Heapq.Bisect.Weakref.Copy以及Pprint这些数据结构的用法,需要的朋友可以参考 ...
 - NoSQL之Redis高级实用命令详解--安全和主从复制
		
Android IOS JavaScript HTML5 CSS jQuery Python PHP NodeJS Java Spring MySQL MongoDB Redis NOSQL Vim ...
 - Android 高级UI设计笔记07:RecyclerView 的详解
		
1. 使用RecyclerView 在 Android 应用程序中列表是一个非常重要的控件,适用场合非常多,如新闻列表.应用列表.消息列表等等,但是从Android 一出生到现在并没有非常 ...
 - Scala进阶之路-Scala高级语法之隐式(implicit)详解
		
Scala进阶之路-Scala高级语法之隐式(implicit)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们调用别人的框架,发现少了一些方法,需要添加,但是让别人为你一 ...
 - Kotlin——高级篇(二):高阶函数详解与标准的高阶函数使用
		
在上面一个章节中,详细的讲解了Kotlin中关于Lambda表达式的语法以及运用,如果还您对其还不甚理解,请参见Kotlin--高级篇(一):Lambda表达式详解.在这篇文章中,多次提到了Kotli ...
 - Jmeter 正则表达式提取器详解(Regular Expression Exactor)
		
Jmeter 正则表达式提取器详解(Regular Expression Exactor) Name(名称):随意设置,最好有业务意义. Comments(注释):随意设置,可以为空 Apply to ...
 
随机推荐
- go 通过指针修改结构体小写字段的值
			
package main import ( "fmt" "unsafe" ) type W struct { b int32 c int64 } func ma ...
 - golang beego 使用supervisor 部署后台进程管理. 静态文件找不到的解决办法.
			
directory=/root/go/src/you_self_dir 请在客户端配置文件*.ini中加入一行命令, 等于号后面就是自己的项目目录,这时就能找到项目文件了.
 - nginx aio模块添加与配置
			
1. 升级目的 让现有服务平滑过渡到高版本,减少服务漏洞,提高服务性能 让其支持nginx最新特性 nginx threads模块 2. 获取nginx1.7.2版本 wget http://ngin ...
 - scala怎么退出
			
scala怎么退出 scala> :help //查看帮助 All commands can be abbreviated, e.g., :he instead of :help. :edit ...
 - 原来Stable Diffusion是这样工作的
			
stable diffusion是一种潜在扩散模型,可以从文本生成人工智能图像.为什么叫做潜在扩散模型呢?这是因为与在高维图像空间中操作不同,它首先将图像压缩到潜在空间中,然后再进行操作. 在这篇文章 ...
 - map和解构赋值的拷贝
			
map遍历之后返回的数组是深拷贝,解构赋值一维的是深拷贝,二维是浅拷贝
 - fs.1.10 ON rockylinux8 docker镜像制作
			
概述 freeswitch是一款简单好用的VOIP开源软交换平台. rockylinux docker上编译安装fs1.10版本的流程记录. 环境 docker engine:Version 24.0 ...
 - ABC319题解
			
直接从 D 开始了. D 可可爱爱的二分捏. check 就按照题目里写的就行了. 然后 \(l\) 的初值要注意一下,就是 \(\max^{i \le n}_{i=1}a_i\). 代码: #inc ...
 - Externalizable接口实现序列化与反序列化
			
Externalizable接口实现序列化与反序列化 package com.example.core.mydemo.java; import com.example.core.mydemo.json ...
 - PhantomReference 和 WeakReference 究竟有何不同
			
本文基于 OpenJDK17 进行讨论,垃圾回收器为 ZGC. 提示: 为了方便大家索引,特将在上篇文章 <以 ZGC 为例,谈一谈 JVM 是如何实现 Reference 语义的> 中讨 ...