博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linq to Entity经验:表达式转换(修正版本)
阅读量:5319 次
发布时间:2019-06-14

本文共 3430 字,大约阅读时间需要 11 分钟。

      之前有篇文章()我分享过表达式转换的问题,当时以为问题解决了,但后来实际应用中发现其实没有解决,也并非完全没有解决,只不过不实用,问题如下:

 
      在讲问题之前,先来看看表达式转换的目的

      其实我们这么费劲的进行表达式转换,就是为了让我们在UI层写的表达式树条件能够最终转换成EntityFramework能够识别的表达式,之所以需要转换,那是因为EntityFramework只能识别EntityFramework自身的实体对象,如果我们是数据库优先的话,这些实体就是自动生成的,而我们的业务系统往往有独立于数据库的业务实体,这两者有些情况下相同,有些情况下非常不相同,这是我们需要转换的原因,比如TestExpression.Model.Courier是业务实体,My.FrameWork.Utilities.Test.Courier是数据库对象,看如下需求:

      UI层进行数据搜索数据的条件搜集:
      

Expression<Func<TestExpression.Model.Courier, 
bool>> two2 = c => c.Value.StartsWith(mes.Name);

 

      要想进行数据库查询,就需要将上面的表达式树转换成下面EntityFramework能够识别的表达式树。注意,这两个表达式虽然对象名称一样,但命名空间不同。

  

Expression<Func<My.FrameWork.Utilities.Test.Courier, 
bool>> two2 = c => c.Value.StartsWith(mes.Name);

  

      问题:

      StartsWith的参数是字符串,我们知道这个字符串和其它大多数基元数据不一样,它不是值类型,而是引用类型,所以当外面的局部变量做为参数传递到表达式中时,在表达式树进行解析时还会发生MemberAccess,而上篇文章中只支持参数类型是ConstantExpression,而这里的MemberExpression在解析时就是报异常。,比如下面的表达式是可以转换的,直接传递"min",这就是一个ConstantExpression类型的参数:

  

Expression<Func<TestExpression.Model.Courier, 
bool>> two2 = c => c.Value.StartsWith(
"
min
");

 

      但如果这样写就不行:

  

  string name=
"
min
";
  Expression<Func<TestExpression.Model.Courier, 
bool>> two2 = c => c.Value.StartsWith(name);

     

      以及文章开关提到的表达式也不行:(下面的mes是一个class)

Expression<Func<TestExpression.Model.Courier, 
bool>> two2 = c => c.Value.StartsWith(mes.Name);

       

     如何解决?这里分享下我解决问题的过程。
 
    1:既然参数是ConstantExpression的情况可行,那么能否将原本不是ConstantExpression的参数变更为ConstantExpression。
        有了这个想法,当时认为既然mes.Name是一个对象的属性,如果对这个属性值进行下字符串的深度复制是不是也就摆脱class的引用类型问题了,虽然摆脱了class的问题,但发现string本身就是一个引用类型,所以无论是否转换还是没能将非ConstantExpression转换成ConstantExpression的情况,也就是说下面的处理是徒劳无功的。

 

    public 
static 
class StringDeepClone
    {
        
public 
static 
string GetStringDeepClone(
this 
string source)
        {
            
string result = 
string.Empty;
            
using (MemoryStream ms = 
new MemoryStream())
            {
                BinaryFormatter bf = 
new BinaryFormatter();
                bf.Serialize(ms, source );
                source = 
null;
                ms.Position = 
0;
                result = ((
string)bf.Deserialize(ms));
            }
            
return result;
        }
    }

    2:既然引用类型的问题是会发生MemberAccess,那么是否能够解析带局部变量的MemberExpression?
    

Expression<Func<TestExpression.Model.Courier, 
bool>> two2 = c => c.Value.StartsWith(mes.Name);

        像这行代码,在整个表达式的上下文中是没有mes类型信息的,它是一个外部的局部变量,这样在解析时就会增大难度,后来我咨询过脑袋(),他给我提供了一个转换方法,后来由于代码不全就没继续研究了,代码的功能是可以解析这种带局部变量的MemberExpression。

    
    3:既然解析MemberExpression没能成功,那么是否能够从MemberExpression中直接抽取出值来呢?
         在网上搜索了一番,发现一老外提供了一个方法能够解决:
     

        private 
static 
object GetMemberExpressionValue(MemberExpression member)
        {
            
var objectMember = Expression.Convert(member, 
typeof(
object));
            
var getterLambda = Expression.Lambda<Func<
object>>(objectMember);
            
var getter = getterLambda.Compile();
            
return getter();
        }
        

        这个方法的思路就是将MemberExpression转换成可执行的Lambda表达式,通过执行Lambda表达式得到计算值。
    
     有了上面的方法,我们需要修改下原来的解析方法:主要是判断参数表达式的类型,如果是值类型就走以前的逻辑,如果是引用类型,我们需要从MemberExpression中抽取数值,有了数据我们的问题就解决了。
                    

                   case ExpressionType.Call:
                    {
                        
var be = (MethodCallExpression)node;
                        
var resultValue = 
string.Empty;
                        
switch (be.Arguments[
0].NodeType)
                        {
                            
case ExpressionType.MemberAccess:
                                
var exprssion = GetMemberExpressionValue((MemberExpression)be.Arguments[
0]);
                                
if (
null != exprssion)
                                {
                                    resultValue = exprssion.ToString();
                                }
                                
break;
                            
case ExpressionType.Constant:
                                resultValue=((ConstantExpression)be.Arguments[
0]).Value.ToString();
                                
break;
                        }
                        
var expression = GetMethodExpression((MemberExpression)be.Object, resultValue, be.Method.Name, subst);
                        
return expression.Body;
                    }

                    

      到此,表达式树的转换终于可以在实际项目应用了,这对我们动态搜集查询条件非常有帮助,尽管这个问题困扰我超过半个月时间,但最终还是得到解决了。表达式的转换比较麻烦,如果想让自己的表达式转换功能越来越强大,那么我们需要针对不同的情况编写对应的解决方案才行,没有完美只有最适合。
 

转载于:https://www.cnblogs.com/ASPNET2008/archive/2012/11/11/2765514.html

你可能感兴趣的文章
学习AS3菜鸟起飞吧之—函数(二):函数之返回语句
查看>>
sap basis 常用事务码 --转
查看>>
迭代器
查看>>
传入值参数&传入引用参数的区别
查看>>
第13课 - 自动生成依赖关系(下)
查看>>
POJ No.2386【B007】
查看>>
点击复制插件clipboard.js
查看>>
LeetCode : Pascal's Triangle
查看>>
mysql优化
查看>>
[Kali_BT]通过低版本SerialPort蓝牙渗透功能手机
查看>>
Oracle命令--创建表空间、创建临时表空间、创建用户
查看>>
poj2187 Beauty Contest
查看>>
cf 472G Design Tutorial: Increase the Constraints 分块+压位/FFT
查看>>
iOS开发之使用XMPPFramework实现即时通信(一)
查看>>
CentOS 6.5(x86_32)下安装Oracle 10g R2
查看>>
C语言学习总结(三) 复杂类型
查看>>
数据类型转换
查看>>
HNOI2018
查看>>
Android中检测网络连接状况的方法
查看>>
【理财】关于理财的网站
查看>>