- 温习访问者模式,简单的说就是,按照既定的路线去一步一步访问,在访问中间步骤做点其他扩展!
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 SQL
var dbSet = new List<People>().AsQueryable();//EF DbSet
dbSet.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;
}
}