Linq to Entity经验:表达式转换(修正版本)

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


      其实我们这么费劲的进行表达式转换,就是为了让我们在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类型信息的,它是一个外部的局部变量,这样在解析时就会增大难度,后来我咨询过脑袋(http://cnblogs.com/ninputer),他给我提供了一个转换方法,后来由于代码不全就没继续研究了,代码的功能是可以解析这种带局部变量的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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值