对于Expression总感觉那么深奥,虽然现在也是。但是终究是略窥门径了。

一,先说我这两天遇到的问题:

条件:

1. 一个Patient有多个Case,一个Case有多个Sample。

2. 一个Patient有多个PatientCustomTableRecord,一个Case有多个CaseCustomTableRecord,一个Sample有多个SampleCustomTableRecord。

3. CustomTable的结构是:一个CustomTableIndex有多个CustomTableColumn。每一个CustomTableRecord对应一个Column,值类型有5种,int, double, string, datetime, 数据字典。如果值来源于数据字典的话还要记录数据字典ID。

4. 每个CustomTableRecord有个属性是RecordIndex,表明该值是第几条记录。

所以唯一确定一条CustomRecord的方法是,锁定该Record所在的Column,根据该Column的值类型,锁定值。

5. 具体条件:有个CustomTableIndex叫IHC,属于Sample的。其中有两列:IHC指标名称,IHC指标结果

期待查询:

1. 具有IHC指标名称=IHC1 且IHC指标结果=20%的Sample

2. 具有条件1那样Sample的Case

3. 具有条件2那样的Patient

 三、动态排序

            ParameterExpression customerParam = Expression.Parameter(typeof(tb_goods), "c");
            Expression<Func<tb_goods, int>> outerKeySelector = Expression.Lambda<Func<tb_goods, int>>(
            Expression.Convert(Expression.PropertyOrField(customerParam, "id"), typeof(int)), customerParam);

            int outTotal = 0;
            var pagelist = EPcms.Models.LinqHelper<EPDbContext>.Select_Where_Page<tb_goods, int>(ex, 20, 1, ref outTotal,outerKeySelector);


二,解决方法。

采用动态拼接查询语句的方式。利用Expression.Call, Expression.Lambda等等利用Queryable的Any,Where,Select,Contains等等方法

三,阐述一下自己理解的Expression的一些用法:

1. Expression.Call: msdn上有。还总是看不明白。现在拿个例子来解释:

比如我想Call,Queryable的Any方法:去这个类里面看,有两种定义:

[csharp] view plaincopy
  1. public static bool Any<TSource>(this IQueryable<TSource> source);  
  2.   
  3. public static bool Any<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);  

首先说,如果方法是带T的泛型,那么在call的时候必须指定Type[],如果有多个Type,那么那个Type[]里的成员就有多少个。

Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(SampleCustomTableRecord) }, leftwhere)  

这个语句返回的是一个bool型的Expression。其中leftwhere是一个IQueryable<SampleCustomTableRecord>类型的Expression。没有任何predicate。就是要找出满足任意leftwhere里面条件的SampleCustomTableRecord。然后用在我之后要用的Where里。

我调用第二种Any的时候会这么写:

Expression temp = Expression.Convert(Expression.PropertyOrField(Expression.PropertyOrField(Expression.PropertyOrField(pe_Sample, "ParentCase"), "ParentPatient"), "CustomTableRecords"), typeof(IQueryable<PatientCustomTableRecord>));  

Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(PatientCustomTableRecord) }, temp, result)  

这里的temp代表含有当前sample的case的patient的PatientCustomTableRecord集合。Result代表一个lambdaExpression,就是:Expression<Func<TSource, bool>> predicate。定义Lambda的时候,如果泛型,要指定ParameterExpression作为TSource。比如实现这个Lambda:

SCTR => ((SCTR.CHTI.ID == 131) And (SCTR.Column.ID == 73))

SCTR就是那个ParameterExpression:

public static ParameterExpression pe = Expression.Parameter(typeof(SampleCustomTableRecord), "SCTR");  

自我理解:这里的SCTR类似于SQL里面的AS后面的别称,我就叫他委托对象名
//首先找到SCTR.Column.ID属性  
  
Expression cCIDExp = Expression.Property(Expression.Property(pe, "Column"), "ID");  
  
Expression findCCExp = Expression.Equal(cCIDExp, Expression.Constant(73));  
  
//然后锁定数据字典外键  
  
Expression left = Expression.PropertyOrField(Expression.Property(pe, "CHTI"), "ID");  
  
left = Expression.Equal(left, Expression.Constant(131));  
  
//最后实现Lambda委托. 这个委托的最终类型是://Expression<Func<SampleCustomRecord, bool>>  
  
Expression rv = Expression.Lambda(Expression.And(left, findCCExp), pe);  

这样就完成了两个Any的调用。

2. 想做join。实现题中查询

为此在论坛上发帖:http://topic.csdn.net/u/20120101/18/a73b5d2f-5d31-4434-aae7-503b0aa25691.html
到现在位置虽没有解决,但是开阔了我的思路。目前我想要的Linq是这样的:

var query=from ain SampleCustomTableRecordsjoin bin SampleCustomTableRecordson a.RecordIndexequalsb.RecordIndexwhere a.FK1==73&& a.FK2==131&& b.FK1==74&& b.FK2=163select a;

可惜这里的join我必须用Queryable的join才能实现,关于这个Join的使用,我google到了一篇文章,现在给他贴过来:

OK - let's say you want to build the following query:

from c in Customers  
 join p in Purchases on c.ID equals p.CustomerID  
 select c.Name + " bought a " + p.Description  

The first step is to translate this into lambda/method syntax. We can do this with LINQPad:(linqpad是个好东东。我得找找)
Customers  
 .Join (  
    Purchases,   
    c => (Int32?)(c.ID),   
    p => p.CustomerID,   
    (c, p) => ((c.Name + " bought a ") + p.Description)  
 )  

动态排序:

ParameterExpression customerParam = Expression.Parameter (typeof (Customer), "c");  
   
 Expression<Func<Customer, int?>> outerKeySelector = Expression.Lambda<Func<Customer, int?>> (  
     Expression.Convert (Expression.PropertyOrField (customerParam, "ID"), typeof (int?)),   
     customerParam);  
       
 ParameterExpression purchaseParam = Expression.Parameter (typeof (Purchase), "p");      
   
 Expression<Func<Purchase, int?>> innerKeySelector = Expression.Lambda<Func<Purchase, int?>> (  
     Expression.PropertyOrField (purchaseParam, "CustomerID"),   
     purchaseParam);      

MethodInfo concatThreeItems = typeof (string).GetMethod ("Concat", Enumerable.Repeat (typeof (object), 3).ToArray());  
        
  Expression<Func<Customer, Purchase, string>> resultSelector = Expression.Lambda<Func<Customer, Purchase, string>> (  
      Expression.Call (  
          concatThreeItems,  
          new Expression[]  
          {  
              Expression.PropertyOrField (customerParam, "Name"),   // c.Name  
              Expression.Constant (" bought a "),  
              Expression.PropertyOrField (purchaseParam, "Description")  // p.Description  
          }),  
      customerParam,  
      purchaseParam);  

We can test this as follows:

[csharp] view plaincopy
  1. Expression.Lambda<Func<IQueryable<string>>> (join).Compile()().Dump();  

 

也许对于Expression Linq达人来讲这算不了什么,可对于我来说真的很神奇。现在就差一步了:Join后怎么加Where?Join里肯定有两个委托对象名,我怎么保证这两个对象名还能在Where里面用?这里的Where可不再是Linq里的where了啊。有个哥们跟我一样的问题,给了我思路启发:http://stackoverflow.com/questions/485398/how-to-create-a-join-in-an-expression-tree-for-linq。我何必非得在Join后加Where呢?直接在Join的左右先做Where不就行了?不断join只需要不停滴更换右边的委托对象名就行了

懒得说过程了,直接上代码:这个是想找出满足SampleCustomTableRecord条件的Sample

[csharp] view plaincopy
  1. else if (item.peRecordType == pe_SampleCustomTableRecord)  
  2.                             {  
  3.                                 //handle the first one, the very beginning on left hand  
  4.                                 string[] firstsplit = item.SearchConditions[0].FieldTitle.Split(new char[]{'-'});  
  5.                                 Expression left = GetCustomPredict(pe_SampleCustomTableRecord, "样本信息", mtype, firstsplit[1], firstsplit[2], item.SearchConditions[0].Condition, item.SearchConditions[0].Value);//子函数用于得到应用于Where条件的Lambda表达式  
  6.                                 Expression leftwhere = Expression.Call(typeof(Queryable), "Where"new Type[] { typeof(SampleCustomTableRecord) }, sampleCR, left);  
  7.                                 for (int i = 1; i < item.SearchConditions.Count; i++)  
  8.                                 {  
  9.                                     ParameterExpression rightpe = Expression.Parameter(typeof(SampleCustomTableRecord), "SCTR"+i);//由于是同表Join,所以右边的委托对象名需要不停更换  
  10.                                     string[] secondsplit = item.SearchConditions[i].FieldTitle.Split(new char[]{'-'});  
  11.                                     Expression right = GetCustomPredict(rightpe, "样本信息", mtype, secondsplit[1], secondsplit[2], item.SearchConditions[i].Condition, item.SearchConditions[i].Value);  
  12.                                     Expression rightwhere = Expression.Call(typeof(Queryable),"Where",new Type[]{typeof(SampleCustomTableRecord)},sampleCR,right);  
  13.                                     Expression OuterKeySelector = Expression.Lambda<Func<SampleCustomTableRecord, int>>(Expression.Convert(Expression.PropertyOrField(pe_SampleCustomTableRecord, "RecordIndex"), typeof(int)), pe_SampleCustomTableRecord);  
  14.                                     Expression InnerKeySelector = Expression.Lambda<Func<SampleCustomTableRecord, int>>(Expression.Convert(Expression.PropertyOrField(rightpe, "RecordIndex"), typeof(int)), rightpe);  
  15.                                     Expression<Func<SampleCustomTableRecord,SampleCustomTableRecord,SampleCustomTableRecord>> resultSelector = Expression.Lambda<Func<SampleCustomTableRecord,SampleCustomTableRecord,SampleCustomTableRecord>>(pe_SampleCustomTableRecord,new ParameterExpression[]{pe_SampleCustomTableRecord,rightpe});  
  16.                                     Expression test = Expression.Call(typeof(Queryable), "Join",  
  17.                                            new Type[]  
  18.                                         {  
  19.                                             typeof(SampleCustomTableRecord),//TOuter  
  20.                                             typeof(SampleCustomTableRecord),//TInner  
  21.                                             typeof(int),//TKey  
  22.                                             typeof(SampleCustomTableRecord)//TResult  
  23.                                         },  
  24.                                            new Expression[]  
  25.                                         {  
  26.                                             leftwhere,  
  27.                                             rightwhere,  
  28.                                             OuterKeySelector,//outerkey  
  29.                                             InnerKeySelector,//innerkey  
  30.                                             resultSelector//result????  
  31.                                         });  
  32.                                     leftwhere = test;  
  33.                                 }  
  34.                                 CombinationExpressions.Add(Expression.Call(typeof(Queryable), "Any"new Type[] { typeof(SampleCustomTableRecord) }, leftwhere));  
  35. }  


得到的Expression是类似于这样的:

test:
{Convert(Sample.CustomTableRecords).Where(SampleCustomTableRecord => ((SampleCustomTableRecord.CHTI.ID == 131) And

(SampleCustomTableRecord.Column.ID == 73)))
.Join(Convert(Sample.CustomTableRecords).Where(SCTR1 => ((SCTR1.CHTI.ID == 163) And (SCTR1.Column.ID == 74))),

SampleCustomTableRecord => Convert(SampleCustomTableRecord.RecordIndex), 
SCTR1 => Convert(SCTR1.RecordIndex), 
(SampleCustomTableRecord, SCTR1) => SampleCustomTableRecord)}

 

到此为止,成功已有90%。剩下的就是利用Where,Select,Contains,拼接最终步骤,来实现命题的查询了。

就写到这吧,免的日后忘了。记性越来越差了。

helpful link:

http://msdn.microsoft.com/en-us/library/bb896266.aspx

http://msdn.microsoft.com/en-us/library/bb397676.aspx

http://msdn.microsoft.com/en-us/library/bb534644.aspx

http://msdn.microsoft.com/en-us/library/bb882637.aspx

 http://blogs.msdn.com/b/mattwar/archive/2007/09/04/linq-building-an-iqueryable-provider-part-vii.aspx 两种方法实现join

 

 

2012-2-29补充:

速成的果然不行,时隔不到两个月,我居然看不懂自己的代码了。盯了一个小时又悟出点以前知道但没写到的内容:

1. 我的一个ResultSelector是这样写的:

[csharp] view plaincopy
  1. Expression<Func<SampleCustomTableRecord, SampleCustomTableRecord, SampleCustomTableRecord>> resultSelector = Expression.Lambda<Func<SampleCustomTableRecord, SampleCustomTableRecord, SampleCustomTableRecord>>(pe_SampleCustomTableRecord, new ParameterExpression[] { pe_SampleCustomTableRecord, rightpe });  

此处Lambda的定义为:

[csharp] view plaincopy
  1. //  
  2.         // Summary:  
  3.         //     Creates an System.Linq.Expressions.Expression<TDelegate> where the delegate  
  4.         //     type is known at compile time.  
  5.         //  
  6.         // Parameters:  
  7.         //   body:  
  8.         //     An System.Linq.Expressions.Expression to set the System.Linq.Expressions.LambdaExpression.Body  
  9.         //     property equal to.  
  10.         //  
  11.         //   parameters:  
  12.         //     An array of System.Linq.Expressions.ParameterExpression objects to use to  
  13.         //     populate the System.Linq.Expressions.LambdaExpression.Parameters collection.  
  14.         //  
  15.         // Type parameters:  
  16.         //   TDelegate:  
  17.         //     A delegate type.  
  18.         //  
  19.         // Returns:  
  20.         //     An System.Linq.Expressions.Expression<TDelegate> that has the System.Linq.Expressions.Expression.NodeType  
  21.         //     property equal to System.Linq.Expressions.ExpressionType.Lambda and the System.Linq.Expressions.LambdaExpression.Body  
  22.         //     and System.Linq.Expressions.LambdaExpression.Parameters properties set to  
  23.         //     the specified values.  
  24.         //  
  25.         // Exceptions:  
  26.         //   System.ArgumentNullException:  
  27.         //     body is null.-or-One or more elements in parameters are null.  
  28.         //  
  29.         //   System.ArgumentException:  
  30.         //     TDelegate is not a delegate type.-or-body.Type represents a type that is  
  31.         //     not assignable to the return type of TDelegate.-or-parameters does not contain  
  32.         //     the same number of elements as the list of parameters for TDelegate.-or-The  
  33.         //     System.Linq.Expressions.Expression.Type property of an element of parameters  
  34.         //     is not assignable from the type of the corresponding parameter type of TDelegate.  
  35.         public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters);  

 

debug时看到resultSelector变成:

{(SampleCustomTableRecord, SCTR1) => SampleCustomTableRecord}

此时:Lambda的两个Parameter就是

pe_SampleCustomTableRecord=Expression.Parameter(typeof(SampleCustomTableRecord), "SampleCustomTableRecord");

rightpe=Expression.Parameter(typeof(SampleCustomTableRecord), "SCTR" + i);


这里的Func做如下解释:Func<Tin1, Tin2, Tout>. 显然两个In,对应两个委托对象名,即两个ParameterExpression.

Lambda就是(委托对象名)=>{Body真正的函数体}

http://blog.csdn.net/hoken2020856/article/details/7173066

本文转载:CSDN博客