- 温习访问者模式,简单的说就是,按照既定的路线去一步一步访问,在访问中间步骤做点其他扩展!
expression应用: ```csharp //为什么要使用表达式目录树来拼装解析呢; //可以提高重用性; //如果封装好一个方法,接受一个表达式目录树,在解析的时候,其实就是不断的访问,访问有规则; //任何一个表达式目录树我都可以调用当前方法来解析; //表达式目录树可以支持泛型;封装的时候个人认为好封装一些;
//应用1:把表达式拼装成sql {
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);Console.WriteLine(vistor.Condition());}/// <summary>/// 表达式树转化成Sql,首先继承 ExpressionVisitor,该类的Visit方法就是自动识别该表达式的形式,分配给适合的方法做解析/// </summary>public class ConditionBuilderVisitor : ExpressionVisitor{private Stack<string> _StringStack = new Stack<string>();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(" " + node.NodeType.ToSqlOperator() + " ");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");//this._StringStack.Push(" [" + node.Member.Name + "] ");////return node;if (node.Expression is ConstantExpression){var value1 = this.InvokeValue(node);var value2 = this.ReflectionValue(node);//this.ConditionStack.Push($"'{value1}'");this._StringStack.Push("'" + value2 + "'");}else{this._StringStack.Push(" [" + node.Member.Name + "] ");}return node;}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;}}
```csharp先插入一个场景:根据条件搜索{//现在是Linq to SQLvar dbSet = new List<People>().AsQueryable();//EF DbSetdbSet.Where(p => p.Age == 25 & p.Name.Contains("李四"));Expression<Func<People, bool>> exp = null;Console.WriteLine("用户输入个名称,为空就跳过");string name = Console.ReadLine();if (!string.IsNullOrWhiteSpace(name)){exp = p => p.Name.Contains(name);}Console.WriteLine("用户输入个最小年纪,为空就跳过");string age = Console.ReadLine();if (!string.IsNullOrWhiteSpace(age) && int.TryParse(age, out int iAge)){exp = p => p.Age > iAge;}}}//提问://上面的玩法是不是只有最后一个条件才生效?//如果需要两个条件都满足呢? 怎么解决,可以做到表达式目录树的合并(且) 或 非//解答:当然是拼装啊;拼装可以从最小粒度来组装表达式目录树~~如果有一个封装,你把各种条件给我,我从最小粒度开始一个一个的拼装起来,不就是一个长的表达式目录树了吗?
//应用2: 表达式链接Expression<Func<People, bool>> lambda1 = x => x.Age > 5;Expression<Func<People, bool>> lambda2 = x => x.Id > 5;//现有表达式数lambda1,lambda2如何有目的把他们连接起来//Expression<Func<People, bool>> newExpress = x => x.Age > 5 && 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();//非Do1(lambda3);Do1(lambda4);Do1(lambda5);//验证方法private static void Do1(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();}/// <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){//return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, expr2.Body), expr1.Parameters);ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);var left = visitor.Replace(expr1.Body);var right = visitor.Replace(expr2.Body); //为了能够生成一个新的表达式目录树var 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){ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");NewExpressionVisitor visitor = new NewExpressionVisitor(newParameter);var left = visitor.Replace(expr1.Body);var right = visitor.Replace(expr2.Body);var body = Expression.Or(left, right);return Expression.Lambda<Func<T, bool>>(body, newParameter);}public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr){var candidateExpr = expr.Parameters[0];var body = Expression.Not(expr.Body);return Expression.Lambda<Func<T, bool>>(body, candidateExpr);}}/// <summary>/// 建立新表达式/// </summary>internal class NewExpressionVisitor : ExpressionVisitor{public ParameterExpression _NewParameter { get; private set; }public NewExpressionVisitor(ParameterExpression param){this._NewParameter = param;}public Expression Replace(Expression exp){return this.Visit(exp);}protected override Expression VisitParameter(ParameterExpression node){return this._NewParameter;}}
