不成文的,我这两天学习Expression的零散心得

对于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

 

二,解决方法。

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

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

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

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

public static bool Any<TSource>(this IQueryable<TSource> source);

public static bool Any<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

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

因此我调用第一种Any的时候会这么写:(2/29更正:这实际上是第二种Any)

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.RecordIndexequals b.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)
   )



(Note the cast to the nullable int in the 4th line: this is necessary if the foreign key is nullable).

To build the expression tree manually, we start by building the three lambda expressions in the query. The first two are fairly easy:
 

   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);    


The third one requires that we call string.Concat:

  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);


Now we can do the join. We must supply the type of each type argument in Join. The Join method is defined as follows:

    public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>

Therefore, we must supply the type arguments in the order TOuter, TInner, TKey, TResult:

   var db = new TypedDataContext();
    var join =
        Expression.Call (
            typeof (Queryable),
            "Join",
            new Type[]
            {
                typeof (Customer),   // TOuter,
                typeof (Purchase),   // TInner,
                typeof (int?),       // TKey,
                typeof (string)      // TResult
            },
            new Expression[]
            {
                Expression.PropertyOrField (Expression.Constant (db), "Customers"),
                Expression.PropertyOrField (Expression.Constant (db), "Purchases"),
                outerKeySelector,
                innerKeySelector,
                resultSelector
            });


We can test this as follows:

    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

else if (item.peRecordType == pe_SampleCustomTableRecord)
                            {
                                //handle the first one, the very beginning on left hand
                                string[] firstsplit = item.SearchConditions[0].FieldTitle.Split(new char[]{'-'});
                                Expression left = GetCustomPredict(pe_SampleCustomTableRecord, "样本信息", mtype, firstsplit[1], firstsplit[2], item.SearchConditions[0].Condition, item.SearchConditions[0].Value);//子函数用于得到应用于Where条件的Lambda表达式
                                Expression leftwhere = Expression.Call(typeof(Queryable), "Where", new Type[] { typeof(SampleCustomTableRecord) }, sampleCR, left);
                                for (int i = 1; i < item.SearchConditions.Count; i++)
                                {
                                    ParameterExpression rightpe = Expression.Parameter(typeof(SampleCustomTableRecord), "SCTR"+i);//由于是同表Join,所以右边的委托对象名需要不停更换
                                    string[] secondsplit = item.SearchConditions[i].FieldTitle.Split(new char[]{'-'});
                                    Expression right = GetCustomPredict(rightpe, "样本信息", mtype, secondsplit[1], secondsplit[2], item.SearchConditions[i].Condition, item.SearchConditions[i].Value);
                                    Expression rightwhere = Expression.Call(typeof(Queryable),"Where",new Type[]{typeof(SampleCustomTableRecord)},sampleCR,right);
                                    Expression OuterKeySelector = Expression.Lambda<Func<SampleCustomTableRecord, int>>(Expression.Convert(Expression.PropertyOrField(pe_SampleCustomTableRecord, "RecordIndex"), typeof(int)), pe_SampleCustomTableRecord);
                                    Expression InnerKeySelector = Expression.Lambda<Func<SampleCustomTableRecord, int>>(Expression.Convert(Expression.PropertyOrField(rightpe, "RecordIndex"), typeof(int)), rightpe);
                                    Expression<Func<SampleCustomTableRecord,SampleCustomTableRecord,SampleCustomTableRecord>> resultSelector = Expression.Lambda<Func<SampleCustomTableRecord,SampleCustomTableRecord,SampleCustomTableRecord>>(pe_SampleCustomTableRecord,new ParameterExpression[]{pe_SampleCustomTableRecord,rightpe});
                                    Expression test = Expression.Call(typeof(Queryable), "Join",
                                           new Type[]
                                        {
                                            typeof(SampleCustomTableRecord),//TOuter
                                            typeof(SampleCustomTableRecord),//TInner
                                            typeof(int),//TKey
                                            typeof(SampleCustomTableRecord)//TResult
                                        },
                                           new Expression[]
                                        {
                                            leftwhere,
                                            rightwhere,
                                            OuterKeySelector,//outerkey
                                            InnerKeySelector,//innerkey
                                            resultSelector//result????
                                        });
                                    leftwhere = test;
                                }
                                CombinationExpressions.Add(Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(SampleCustomTableRecord) }, leftwhere));
}


得到的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是这样写的:

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

此处Lambda的定义为:

//
        // Summary:
        //     Creates an System.Linq.Expressions.Expression<TDelegate> where the delegate
        //     type is known at compile time.
        //
        // Parameters:
        //   body:
        //     An System.Linq.Expressions.Expression to set the System.Linq.Expressions.LambdaExpression.Body
        //     property equal to.
        //
        //   parameters:
        //     An array of System.Linq.Expressions.ParameterExpression objects to use to
        //     populate the System.Linq.Expressions.LambdaExpression.Parameters collection.
        //
        // Type parameters:
        //   TDelegate:
        //     A delegate type.
        //
        // Returns:
        //     An System.Linq.Expressions.Expression<TDelegate> that has the System.Linq.Expressions.Expression.NodeType
        //     property equal to System.Linq.Expressions.ExpressionType.Lambda and the System.Linq.Expressions.LambdaExpression.Body
        //     and System.Linq.Expressions.LambdaExpression.Parameters properties set to
        //     the specified values.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     body is null.-or-One or more elements in parameters are null.
        //
        //   System.ArgumentException:
        //     TDelegate is not a delegate type.-or-body.Type represents a type that is
        //     not assignable to the return type of TDelegate.-or-parameters does not contain
        //     the same number of elements as the list of parameters for TDelegate.-or-The
        //     System.Linq.Expressions.Expression.Type property of an element of parameters
        //     is not assignable from the type of the corresponding parameter type of TDelegate.
        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真正的函数体}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值