C#-表达式树
<h3> 目录</h3><ol>
<li>表达式树</li>
<li>构建表达式树</li>
<li>使用Expression来进行不同对象的相同名字的属性映射</li>
<li>表达式树构建SQL删选</li>
<li>修改表达式树</li>
<li>构建模拟EF的表达式树解析</li>
<li>连接表达式树</li>
</ol>
<h3>1、表达式树</h3>
<p> 表达式树,在C#中是Expression来定义的,它是一种语法树,或者说是一种数据结构。其主要用于存储需要计算、运算的一种结构,它只提供存储功能,不进行运算。通常Expression是配合Lambda一起使用,lambda可以是匿名方法。Expression可以动态创建。</p>
<p> 声明一个lambda表达式,其中可以指明类型,也可以是匿名方法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Func<int, int, int> func = new Func<int, int, int>((m, n) => m * n + 2);</span>
Func<<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>> func = (m, n) => m * n + <span style="color: rgba(128, 0, 128, 1)">2</span>;</pre>
</div>
<p> 上述代码可以使用Expression来定义:</p>
<div class="cnblogs_code">
<pre>Expression<Func<<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>>> exp = (m, n) => m * n + <span style="color: rgba(128, 0, 128, 1)">2</span>;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">lambda表达式声明表达式树</span></pre>
</div>
<p> Expression的方法体只能是一个整体,不能具有花括号,以下代码是不允许的:</p>
<div class="cnblogs_code">
<pre>Expression<Func<<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>>> exp1 = (m, n) =><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">方法体只能一体</span>
<span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> m * n + <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">;
};</span></pre>
</div>
<p> 上述func和exp执行结果相同:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">int</span> iResult1 = func.Invoke(<span style="color: rgba(128, 0, 128, 1)">3</span>, <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">int</span> iResult2 = exp.Compile().Invoke(<span style="color: rgba(128, 0, 128, 1)">3</span>, <span style="color: rgba(128, 0, 128, 1)">2</span>);</pre>
</div>
<h3>2、构建表达式树</h3>
<p> 上述表达式示例可以通过Expression来自主构建,把m、n定义为ParameterExpression参数,把2定义为常数表达式ConstantExpression,使用Expression的静态方法,表示乘和加:</p>
<div class="cnblogs_code">
<pre>ParameterExpression parameterLeft = Expression.Parameter(<span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">int</span>), <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">m</span><span style="color: rgba(128, 0, 0, 1)">"</span>);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义参数</span>
ParameterExpression parameterRight = Expression.Parameter(<span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">int</span>), <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">n</span><span style="color: rgba(128, 0, 0, 1)">"</span>);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义参数</span>
BinaryExpression binaryMultiply = Expression.Multiply(parameterLeft, parameterRight);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">组建第一步的乘法 </span>
ConstantExpression constant = Expression.Constant(<span style="color: rgba(128, 0, 128, 1)">2</span>, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">int</span>)); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义常数参数</span>
BinaryExpression binaryAdd = Expression.Add(binaryMultiply, constant);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">组建第二步的加法</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> expression = Expression.Lambda<Func<<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>>>(binaryAdd, parameterLeft, parameterRight);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">构建表达式 </span>
<span style="color: rgba(0, 0, 255, 1)">var</span> func = expression.Compile();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">编译为lambda表达式</span>
<span style="color: rgba(0, 0, 255, 1)">int</span> iResult3 = func(<span style="color: rgba(128, 0, 128, 1)">3</span>, <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">int</span> iResult4 = expression.Compile().Invoke(<span style="color: rgba(128, 0, 128, 1)">3</span>, <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">int</span> iResult5 = expression.Compile()(<span style="color: rgba(128, 0, 128, 1)">3</span>, <span style="color: rgba(128, 0, 128, 1)">2</span>);</pre>
</div>
<p> 自主构建Expression是,参数名称的定义,可以不是m、n,可以是其他的a、b或者x、y。</p>
<p> 如何构建一个复杂的表达式树?需要使用到Expression中更多的方法、属性、扩展方法等。首先定义一个类:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> People
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Age { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Name { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> Id;
}</span></pre>
</div>
<p> 基于上面的类,构建表达式: Expression<Func<People, bool>> lambda = x => x.Id.ToString().Equals("5");</p>
<p> 这个示例中,使用到了int自身的ToString()方法,还使用到了字符串的Equals方法。构建过程如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">以下表达式树实现lambda的表达式</span>
Expression<Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda = x => x.Id.ToString().Equals(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">5</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">声明一个参数对象</span>
ParameterExpression parameterExpression = Expression.Parameter(<span style="color: rgba(0, 0, 255, 1)">typeof</span>(People), <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">x</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">查找字段, 并绑定访问参数对象字段(属性)的方法:x.Id</span>
MemberExpression member = Expression.Field(parameterExpression, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(People).GetField(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">以上可以用这个代替</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> temp =Expression.PropertyOrField(parameterExpression, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用字段的ToString方法:x.Id.ToString()</span>
MethodCallExpression method = Expression.Call(member, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">int</span>).GetMethod(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ToString</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">new</span> Type[] { }), <span style="color: rgba(0, 0, 255, 1)">new</span> Expression[<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">]);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用字符串的Equals方法:x.Id.ToString().Equals("5")</span>
MethodCallExpression methodEquals = Expression.Call(method, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">string</span>).GetMethod(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Equals</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">new</span> Type[] { <span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">string</span>) }), <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Expression[]
{
Expression.Constant(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">5</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(<span style="color: rgba(0, 0, 255, 1)">string</span>))<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">与常量进行比较,也可以是参数</span>
<span style="color: rgba(0, 0, 0, 1)">});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建表达式树</span>
ar expression = Expression.Lambda<Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>>(methodEquals, <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ParameterExpression[] {parameterExpression });
</span><span style="color: rgba(0, 0, 255, 1)">bool</span> bResult = expression.Compile().Invoke(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> People()
{
Id </span>= <span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">,
Name </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Nigle</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
Age </span>= <span style="color: rgba(128, 0, 128, 1)">31</span><span style="color: rgba(0, 0, 0, 1)">
});</span></pre>
</div>
<h3>3、<strong>使用Expression来进行不同对象的相同名字的属性映射</strong></h3>
<p> 前面构建了类People,现在我们构建一个新的类PeopleCopy:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> PeopleCopy
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Age { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Name { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> Id;
}</span></pre>
</div>
<p> 现在声明一个People对象,然后对People对象的数据进行拷贝到PeopleCopy新对象中去,直接硬编码的方式:</p>
<p><strong> 1. 硬编码</strong></p>
<div class="cnblogs_code">
<pre>People people = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> People()
{
Id </span>= <span style="color: rgba(128, 0, 128, 1)">11</span><span style="color: rgba(0, 0, 0, 1)">,
Name </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Nigle</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
Age </span>= <span style="color: rgba(128, 0, 128, 1)">31</span><span style="color: rgba(0, 0, 0, 1)">
};
PeopleCopy peopleCopy </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> PeopleCopy()
{
Id </span>=<span style="color: rgba(0, 0, 0, 1)"> people.Id,
Name </span>=<span style="color: rgba(0, 0, 0, 1)"> people.Name,
Age </span>=<span style="color: rgba(0, 0, 0, 1)"> people.Age
};</span></pre>
</div>
<p> 如果这样编写,对于属性或者字段比较多的类,在拷贝时,我们需要编写很多次赋值,代码也会很长。此时,我们能想到的是通过反射的方式进行拷贝:</p>
<p> <strong>2. 反射拷贝</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> TOut Trans<TIn, TOut><span style="color: rgba(0, 0, 0, 1)">(TIn tIn)
{
TOut tOut </span>= Activator.CreateInstance<TOut><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> itemOut <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> tOut.GetType().GetProperties())
{
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> itemIn <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> tIn.GetType().GetProperties())
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (itemOut.Name.Equals(itemIn.Name))
{
itemOut.SetValue(tOut, itemIn.GetValue(tIn));
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
}
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> itemOut <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> tOut.GetType().GetFields())
{
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> itemIn <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> tIn.GetType().GetFields())
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (itemOut.Name.Equals(itemIn.Name))
{
itemOut.SetValue(tOut, itemIn.GetValue(tIn));
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> tOut;
}</span></pre>
</div>
<p> 通过反射,我们可以通过输出类型的属性或者字段去查找原类型对应的属性和字段,然后获取值,并设置值的方式进行赋值拷贝。除此之外,我们还能想到的是深克隆的序列化方式,进行反序列化数据:</p>
<p> <strong>3. 序列化和反序列化</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> SerializeMapper
{
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span><span style="color: rgba(0, 128, 0, 1)">序列化反序列化方式/summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> TOut Trans<TIn, TOut><span style="color: rgba(0, 0, 0, 1)">(TIn tIn)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">采用的是json序列化,也可以采用其他序列化方式</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> JsonConvert.DeserializeObject<TOut><span style="color: rgba(0, 0, 0, 1)">(JsonConvert.SerializeObject(tIn));
}
}</span></pre>
</div>
<p> 前面的三种方法是最为常用的方法,但未使用到本文介绍的表达式树。如何将表达式树与拷贝结合起来?有两种方式【缓存+表达式】,【泛型+表达式】</p>
<p><strong> 4. 缓存+表达式树</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 生成表达式树 缓存
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ExpressionMapper
{
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Dictionary<<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>> _Dic = <span style="color: rgba(0, 0, 255, 1)">new</span> Dictionary<<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">object</span>><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 字典缓存表达式树
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> TOut Trans<TIn, TOut><span style="color: rgba(0, 0, 0, 1)">(TIn tIn)
{
</span><span style="color: rgba(0, 0, 255, 1)">string</span> key = <span style="color: rgba(0, 0, 255, 1)">string</span>.Format(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">funckey_{0}_{1}</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(TIn).FullName, <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TOut).FullName);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">_Dic.ContainsKey(key))
{
ParameterExpression parameterExpression </span>= Expression.Parameter(<span style="color: rgba(0, 0, 255, 1)">typeof</span>(TIn), <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
List</span><MemberBinding> memberBindingList = <span style="color: rgba(0, 0, 255, 1)">new</span> List<MemberBinding><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> item <span style="color: rgba(0, 0, 255, 1)">in</span> <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TOut).GetProperties())
{
MemberExpression property </span>= Expression.Property(parameterExpression, <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TIn).GetProperty(item.Name));
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">绑定Out和In之间的关系:Age = p.Age</span>
MemberBinding memberBinding =<span style="color: rgba(0, 0, 0, 1)"> Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> item <span style="color: rgba(0, 0, 255, 1)">in</span> <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TOut).GetFields())
{
MemberExpression property </span>= Expression.Field(parameterExpression, <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TIn).GetField(item.Name));
MemberBinding memberBinding </span>=<span style="color: rgba(0, 0, 0, 1)"> Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression </span>= Expression.MemberInit(Expression.New(<span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TOut)), memberBindingList.ToArray());
Expression</span><Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>><span style="color: rgba(0, 0, 0, 1)">(memberInitExpression, parameterExpression);
Func</span><TIn, TOut> func = lambda.Compile();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">拼装是一次性的</span>
_Dic =<span style="color: rgba(0, 0, 0, 1)"> func;
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> ((Func<TIn, TOut><span style="color: rgba(0, 0, 0, 1)">)_Dic).Invoke(tIn);
}
}</span></pre>
</div>
<p> <strong> 5. 泛型+表达式</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 生成表达式树泛型缓存
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><typeparam name="TIn"></typeparam></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><typeparam name="TOut"></typeparam></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> ExpressionGenericMapper<TIn, TOut><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Mapper`2</span>
<span style="color: rgba(0, 0, 0, 1)">{
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Func<TIn, TOut> func = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> ExpressionGenericMapper()
{
ParameterExpression parameterExpression </span>= Expression.Parameter(<span style="color: rgba(0, 0, 255, 1)">typeof</span>(TIn), <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">p</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
List</span><MemberBinding> memberBindingList = <span style="color: rgba(0, 0, 255, 1)">new</span> List<MemberBinding><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> item <span style="color: rgba(0, 0, 255, 1)">in</span> <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TOut).GetProperties())
{
MemberExpression property </span>= Expression.Property(parameterExpression, <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TIn).GetProperty(item.Name));
MemberBinding memberBinding </span>=<span style="color: rgba(0, 0, 0, 1)"> Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> item <span style="color: rgba(0, 0, 255, 1)">in</span> <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TOut).GetFields())
{
MemberExpression property </span>= Expression.Field(parameterExpression, <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TIn).GetField(item.Name));
MemberBinding memberBinding </span>=<span style="color: rgba(0, 0, 0, 1)"> Expression.Bind(item, property);
memberBindingList.Add(memberBinding);
}
MemberInitExpression memberInitExpression </span>= Expression.MemberInit(Expression.New(<span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TOut)), memberBindingList.ToArray());
Expression</span><Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ParameterExpression[]
{
parameterExpression
});
func </span>= lambda.Compile();<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">拼装是一次性的</span>
<span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> TOut Trans(TIn t)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> func(t);
}
}</span></pre>
</div>
<p> 除了上述5中方法,还可以使用框架自带的AutoMapper,首先我们要nuget添加引用AutoMapper即可直接使用,具体代码为:</p>
<p><strong> 6. AutoMapper</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> AutoMapperTest
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> TOut Trans<TIn, TOut><span style="color: rgba(0, 0, 0, 1)">(TIn tIn)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> AutoMapper.Mapper.Instance.Map<TOut><span style="color: rgba(0, 0, 0, 1)">(tIn);
}
}</span></pre>
</div>
<p> <strong>测评:</strong>对上述6种方式进行测评,每一种拷贝方式运行100 0000次:</p>
<div class="cnblogs_code">
<pre>Stopwatch watch = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Stopwatch();
watch.Start();
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < <span style="color: rgba(128, 0, 128, 1)">1000000</span>; i++<span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">测试六种方法</span>
PeopleCopy peopleCopy = <span style="color: rgba(0, 0, 255, 1)">new</span> PeopleCopy(){Id = people.Id, Name = people.Name,Age = people.Age}; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">直接赋值的方式复制--22ms
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">PeopleCopy peopleCopy = ReflectionMapper.Trans<People, PeopleCopy>(people); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">反射赋值的方式复制---1573ms
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">PeopleCopy peopleCopy = SerializeMapper.Trans<People, PeopleCopy>(people); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">序列化方式---2716ms
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">PeopleCopy peopleCopy = ExpressionMapper.Trans<People, PeopleCopy>(people); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">表达式树 缓存复制---517ms
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">PeopleCopy peopleCopy = ExpressionGenericMapper<People, PeopleCopy>.Trans(people); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">表达式树泛型缓存--77ms
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">PeopleCopy peopleCopy = AutoMapperTest.Trans<People, PeopleCopy>(people); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">AutoMapper---260ms</span>
<span style="color: rgba(0, 0, 0, 1)">}
watch.Stop();
Console.WriteLine($</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">耗时:{ watch.ElapsedMilliseconds} ms</span><span style="color: rgba(128, 0, 0, 1)">"</span>);</pre>
</div>
<h3>4、表达式树构建SQL删选</h3>
<p> 传统的sql在构建条件语句时,需要通过诸多判断,进而构建成完整的查询语句。</p>
<div class="cnblogs_code">
<pre>People p = new<span> People()
{
Id = 11<span>,
Name = "Nigle"<span>,
Age = 31<span>
};
//拼装sql的方式
string sql = "SELECT * FROM USER WHERE Id=1"<span>;
if (string<span>.IsNullOrWhiteSpace(p.Name))
{
sql += $" and name like '%{p.Name}%'"<span>;
}
sql += $" and age >{p.Age}";</span></span></span></span></span></span></span></pre>
</div>
<p> 事实上,我们偶尔我们会使用linq查询或者lambda表达式,用于条件筛选,如var lambda = x => x.Age > 5; 事实上,我们可以构建上述Expression:</p>
<div class="cnblogs_code">
<pre>People p = new<span> People()
{
Id = 11<span>,
Name = "Nigle"<span>,
Age = 31<span>
};
//拼装表达式树,交给下端用
ParameterExpression parameterExpression = Expression.Parameter(typeof(People), "x");//声明一个参数
Expression propertyExpression = Expression.Property(parameterExpression, typeof(People).GetProperty("Age"));//声明访问参数属性的对象
//Expression property = Expression.Field(parameterExpression, typeof(People).GetField("Id"));
ConstantExpression constantExpression = Expression.Constant(5, typeof(int));//声明一个常量
BinaryExpression binary = Expression.GreaterThan(propertyExpression, constantExpression);//添加比较方法
var lambda = Expression.Lambda<Func<People, bool>>(binary, new ParameterExpression[] { parameterExpression });//构建表达式主体
bool bResult = lambda.Compile().Invoke(p); //比较值</span></span></span></span></pre>
</div>
<h3> 5、修改表达式树</h3>
<p> 本示例将把已经构建完成的表达式树的加法进行修改为减法。修改、拼接、读取节点,需要使用到ExpressionVisitor类,ExpressionVisitor类能动态的解耦,读取相关的节点和方法。</p>
<p> ExpressionVisitor类中的Visit(Expression node)是解读表达式的入口,然后能够神奇的区分参数和方法体,然后将表达式调度到此类中更专用的访问方法中,然后一层一层的解析下去,直到最终的叶节点!</p>
<p> 首先编写OperationsVisitor类,用于修改:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">internal</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> OperationsVisitor : ExpressionVisitor
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Expression Modify(Expression expression)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.Visit(expression);
}
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> Expression VisitBinary(BinaryExpression b)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (b.NodeType ==<span style="color: rgba(0, 0, 0, 1)"> ExpressionType.Add)
{
Expression left </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.Visit(b.Left);
Expression right </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.Visit(b.Right);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Expression.Subtract(left, right);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">.VisitBinary(b);
}
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> Expression VisitConstant(ConstantExpression node)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">.VisitConstant(node);
}
}</span></pre>
</div>
<p> 然后,编写lambda表达式,进行修改并计算结果:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">修改表达式树</span>
Expression<Func<<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>>> exp = (m, n) => m * n + <span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">;
OperationsVisitor visitor </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> OperationsVisitor();
Expression expNew </span>=<span style="color: rgba(0, 0, 0, 1)"> visitor.Modify(exp);
</span><span style="color: rgba(0, 0, 255, 1)">int</span>? iResult = (expNew <span style="color: rgba(0, 0, 255, 1)">as</span> Expression<Func<<span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>, <span style="color: rgba(0, 0, 255, 1)">int</span>>>)?.Compile().Invoke(<span style="color: rgba(128, 0, 128, 1)">2</span>, <span style="color: rgba(128, 0, 128, 1)">3</span>);</pre>
</div>
<p> Visit这个这个方法能够识别出来 m*n+2 是个二叉树,会通过下面的图然后一步一步的进行解析,如果遇到m*n 这会直接调用VisitBinary(BinaryExpression b)这个方法,如果遇到m或者n会调用VisitParameter(ParameterExpression node)这个方法,如果遇到2常量则会调用VisitConstant(ConstantExpression node)。</p>
<p> <strong>ORM与表达式树的关系:</strong></p>
<p><strong> </strong> 经常用到EF,其实都是继承Queryable,然后我们使用的EF通常都会使用 var items = anserDo.GetAll().Where(x => x.OrganizationId == input.oid || input.oid == 0) ,where其实传的就是表达式树。EF写的where等lambda表达式,就是通过ExpressionVisitor这个类来反解析的!后面将构建模拟EF的解析方法。</p>
<h3>6、构建模拟EF的表达式树解析</h3>
<p> 首先,构建解析表达式树的方法,不能再使用默认的。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 表达式树中的访问者
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">internal</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ConditionBuilderVisitor : ExpressionVisitor
{
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 用于存放条件等数据
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">private</span> Stack<<span style="color: rgba(0, 0, 255, 1)">string</span>> _StringStack = <span style="color: rgba(0, 0, 255, 1)">new</span> Stack<<span style="color: rgba(0, 0, 255, 1)">string</span>><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><returns></returns></span>
<span style="color: rgba(0, 0, 255, 1)">internal</span> <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> Condition()
{
</span><span style="color: rgba(0, 0, 255, 1)">string</span> condition = <span style="color: rgba(0, 0, 255, 1)">string</span>.Concat(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._StringStack.ToArray());
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._StringStack.Clear();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> condition;
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 如果是二元表达式
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="node"></param></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><returns></returns></span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> Expression VisitBinary(BinaryExpression node)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (node == <span style="color: rgba(0, 0, 255, 1)">null</span>) <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentNullException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BinaryExpression</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>._StringStack.Push(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">)</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">base</span>.Visit(node.Right);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">解析右边</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>._StringStack.Push(<span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span> + ToSqlOperator(node.NodeType) + <span style="color: rgba(128, 0, 0, 1)">"</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">base</span>.Visit(node.Left);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">解析左边</span>
<span style="color: rgba(0, 0, 255, 1)">this</span>._StringStack.Push(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> node;
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="node"></param></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><returns></returns></span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> Expression VisitMember(MemberExpression node)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (node == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentNullException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MemberExpression</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>._StringStack.Push(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> [</span><span style="color: rgba(128, 0, 0, 1)">"</span> + node.Member.Name + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">] </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> node;
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">.VisitMember(node);
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 将节点类型转换为Sql的操作符
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="type"></param></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><returns></returns></span>
<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> ToSqlOperator(ExpressionType type)
{
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (type)
{
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> (ExpressionType.AndAlso):
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> (ExpressionType.And):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">AND</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> (ExpressionType.OrElse):
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> (ExpressionType.Or):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">OR</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> (ExpressionType.Not):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">NOT</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> (ExpressionType.NotEqual):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"><></span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> ExpressionType.GreaterThan:
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">></span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> ExpressionType.GreaterThanOrEqual:
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">>=</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> ExpressionType.LessThan:
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> ExpressionType.LessThanOrEqual:
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"><=</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span><span style="color: rgba(0, 0, 0, 1)"> (ExpressionType.Equal):
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">=</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Exception(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">不支持该方法</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 常量表达式
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="node"></param></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><returns></returns></span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> Expression VisitConstant(ConstantExpression node)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (node == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentNullException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ConstantExpression</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>._StringStack.Push(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> '</span><span style="color: rgba(128, 0, 0, 1)">"</span> + node.Value + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">' </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> node;
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 方法表达式
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><param name="m"></param></span>
<span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><returns></returns></span>
<span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> Expression VisitMethodCall(MethodCallExpression m)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (m == <span style="color: rgba(0, 0, 255, 1)">null</span>) <span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentNullException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MethodCallExpression</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> format;
</span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (m.Method.Name)
{
</span><span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">StartsWith</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
format </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">({0} LIKE {1}+'%')</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Contains</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
format </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">({0} LIKE '%'+{1}+'%')</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">case</span> <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">EndsWith</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">:
format </span>= <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">({0} LIKE '%'+{1})</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> NotSupportedException(m.NodeType + <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> is not supported!</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.Visit(m.Object);
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.Visit(m.Arguments[<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">]);
</span><span style="color: rgba(0, 0, 255, 1)">string</span> right = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._StringStack.Pop();
</span><span style="color: rgba(0, 0, 255, 1)">string</span> left = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._StringStack.Pop();
</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._StringStack.Push(String.Format(format, left, right));
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> m;
}
}</span></pre>
</div>
<p> 然后,外部就可以通过编写表达式树的查询条件,再通过这个类的实例进行解析成对应的SQL语句:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
Expression</span><Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda = x => x.Age > <span style="color: rgba(128, 0, 128, 1)">5</span> && x.Id > <span style="color: rgba(128, 0, 128, 1)">5</span>
&& x.Name.StartsWith(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span>&& x.Name.EndsWith(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span>&& x.Name.Contains(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">2</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">“ x => x.Age > 5 && x.Id > 5”等同于sql语句</span>
<span style="color: rgba(0, 0, 255, 1)">string</span> sql = <span style="color: rgba(0, 0, 255, 1)">string</span>.Format(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Delete From [{0}] WHERE {1}</span><span style="color: rgba(128, 0, 0, 1)">"</span>, <span style="color: rgba(0, 0, 255, 1)">typeof</span>(People).Name, <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)"> >5 AND >5</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
ConditionBuilderVisitor vistor </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConditionBuilderVisitor();
vistor.Visit(lambda);
Console.WriteLine(vistor.Condition());
}
{
Expression</span><Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda = x => x.Age > <span style="color: rgba(128, 0, 128, 1)">5</span> && x.Name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">A</span><span style="color: rgba(128, 0, 0, 1)">"</span> || x.Id > <span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">;
ConditionBuilderVisitor vistor </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConditionBuilderVisitor();
vistor.Visit(lambda);
Console.WriteLine(vistor.Condition());
}
{
Expression</span><Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda = x => x.Age > <span style="color: rgba(128, 0, 128, 1)">5</span> || (x.Name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">A</span><span style="color: rgba(128, 0, 0, 1)">"</span> && x.Id > <span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">);
ConditionBuilderVisitor vistor </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConditionBuilderVisitor();
vistor.Visit(lambda);
Console.WriteLine(vistor.Condition());
}
{
Expression</span><Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda = x => (x.Age > <span style="color: rgba(128, 0, 128, 1)">5</span> || x.Name == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">A</span><span style="color: rgba(128, 0, 0, 1)">"</span>) && x.Id > <span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">;
ConditionBuilderVisitor vistor </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConditionBuilderVisitor();
vistor.Visit(lambda);
Console.WriteLine(vistor.Condition());
}</span></pre>
</div>
<h3>7、连接表达式树</h3>
<p> 表达式树除了可以修改外,我们还可以通过对其进行表达式树的拼接,将两个及其以上的表达式树进行拼接在一起。先编写一个新的NewExpressionVisitor,继承自ExpressionVisitor,用于拼接时,调用的。它是一个内部类,放在访问拼接类的内部ExpressionExtend。然后再编写对应的扩展方法:Add、Or、Not</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> 合并表达式 And OrNot扩展
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ExpressionExtend
{
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span><span style="color: rgba(0, 128, 0, 1)">合并表达式 expLeft and expRight</span><span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Expression<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> And<T>(<span style="color: rgba(0, 0, 255, 1)">this</span> Expression<Func<T,<span style="color: rgba(0, 0, 255, 1)">bool</span>>> expLeft,Expression<Func<T,<span style="color: rgba(0, 0, 255, 1)">bool</span>>><span style="color: rgba(0, 0, 0, 1)"> expRight)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">用于将参数名进行替换,二者参数不一样</span>
ParameterExpression newParameter = Expression.Parameter(<span style="color: rgba(0, 0, 255, 1)">typeof</span>(T), <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">c</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
NewExpressionVisitor visitor </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> NewExpressionVisitor(newParameter);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">需要先将参数替换为一致的,可能参数名不一样</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> left = visitor.Replace(expLeft.Body);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">左侧的表达式</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> right = visitor.Replace(expRight.Body);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">右侧的表达式</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> body = Expression.And(left, right);<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">合并表达式</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> Expression.Lambda<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>><span style="color: rgba(0, 0, 0, 1)">(body, newParameter);
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span><span style="color: rgba(0, 128, 0, 1)">合并表达式 expr1 or expr2</span><span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Expression<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> Or<T>(<span style="color: rgba(0, 0, 255, 1)">this</span> Expression<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> expr1, Expression<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>><span style="color: rgba(0, 0, 0, 1)"> expr2)
{
ParameterExpression newParameter </span>= Expression.Parameter(<span style="color: rgba(0, 0, 255, 1)">typeof</span>(T), <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">c</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
NewExpressionVisitor visitor </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> NewExpressionVisitor(newParameter);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">需要先将参数替换为一致的,可能参数名不一样</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> left =<span style="color: rgba(0, 0, 0, 1)"> visitor.Replace(expr1.Body);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> right =<span style="color: rgba(0, 0, 0, 1)"> visitor.Replace(expr2.Body);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> body =<span style="color: rgba(0, 0, 0, 1)"> Expression.Or(left, right);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> Expression.Lambda<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>><span style="color: rgba(0, 0, 0, 1)">(body, newParameter);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Expression<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> Not<T>(<span style="color: rgba(0, 0, 255, 1)">this</span> Expression<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>><span style="color: rgba(0, 0, 0, 1)"> expr)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> candidateExpr = expr.Parameters[<span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">];
</span><span style="color: rgba(0, 0, 255, 1)">var</span> body =<span style="color: rgba(0, 0, 0, 1)"> Expression.Not(expr.Body);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> Expression.Lambda<Func<T, <span style="color: rgba(0, 0, 255, 1)">bool</span>>><span style="color: rgba(0, 0, 0, 1)">(body, candidateExpr);
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span><span style="color: rgba(0, 128, 0, 1)">参数替换者 </span><span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> NewExpressionVisitor : ExpressionVisitor
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> ParameterExpression _NewParameter { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> NewExpressionVisitor(ParameterExpression param)
{
</span><span style="color: rgba(0, 0, 255, 1)">this</span>._NewParameter = param;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">用于把参数替换了</span>
<span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span><span style="color: rgba(0, 128, 0, 1)"> 替换</span><span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Expression Replace(Expression exp)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.Visit(exp);
}
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">override</span><span style="color: rgba(0, 0, 0, 1)"> Expression VisitParameter(ParameterExpression node)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回新的参数名</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">._NewParameter;
}
}
}</span></pre>
</div>
<p> 下面是测试代码:</p>
<div class="cnblogs_code">
<pre>Expression<Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda1 = x => x.Age > <span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">;
Expression</span><Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda2 = p => p.Id > <span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">;
Expression</span><Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda3 =<span style="color: rgba(0, 0, 0, 1)"> lambda1.And(lambda2);
Expression</span><Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda4 =<span style="color: rgba(0, 0, 0, 1)"> lambda1.Or(lambda2);
Expression</span><Func<People, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> lambda5 =<span style="color: rgba(0, 0, 0, 1)"> lambda1.Not();
List</span><People> people = <span style="color: rgba(0, 0, 255, 1)">new</span> List<People><span style="color: rgba(0, 0, 0, 1)">()
{
</span><span style="color: rgba(0, 0, 255, 1)">new</span> People(){Id=<span style="color: rgba(128, 0, 128, 1)">4</span>,Name=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">123</span><span style="color: rgba(128, 0, 0, 1)">"</span>,Age=<span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 0, 255, 1)">new</span> People(){Id=<span style="color: rgba(128, 0, 128, 1)">5</span>,Name=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">234</span><span style="color: rgba(128, 0, 0, 1)">"</span>,Age=<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">},
</span><span style="color: rgba(0, 0, 255, 1)">new</span> People(){Id=<span style="color: rgba(128, 0, 128, 1)">6</span>,Name=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">345</span><span style="color: rgba(128, 0, 0, 1)">"</span>,Age=<span style="color: rgba(128, 0, 128, 1)">6</span><span style="color: rgba(0, 0, 0, 1)">},
};
List</span><People> lst1 =<span style="color: rgba(0, 0, 0, 1)"> people.Where(lambda3.Compile()).ToList();
List</span><People> lst2 =<span style="color: rgba(0, 0, 0, 1)"> people.Where(lambda4.Compile()).ToList();
List</span><People> lst3 = people.Where(lambda5.Compile()).ToList();</pre>
</div>
<p> Expression目前只支持ExpressionType的84种操作符Add, AndAlso等等,然后VisitMethodCall这个方法中表示lambda能解析出来的方法名字,如果需要可以自行修改会得到对应的sql语句的where条件!</p>
<p> </p>
</div>
<div id="MySignature" role="contentinfo">
凡所有相,皆是虚妄。<br><br>
来源:https://www.cnblogs.com/pilgrim/p/13860856.html
頁:
[1]