对于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方法:去这个类里面看,有两种定义:
- 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[]里的成员就有多少个。
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:
- 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
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真正的函数体}
http://blog.csdn.net/hoken2020856/article/details/7173066