.net - Combining 2 LambdaExpressions into 1 LamdaExpression -
i dynamically generating expression trees sent off linq entities. providing framework , allow developers specify output columns lambda expressions.
for instance, have column can specify way pull value database is:
p => p.aliases.count()
and column is:
p => p.names.where(n => n.startswith("hello"))
the problem here need combine both of these single expression.
my first attempt was:
getter = field.selectorexpression.body;
and error message the parameter 'p' not bound in specified linq entities query expression
makes since because linq can't know p in p.aliases.count()
.
the next thing tried was:
getter = expression.invoke(field.orderbyselector, new[] {parameter});
however, recieve message the linq expression node type 'invoke' not supported in linq entities.
makes since because don't expect sql server know how run lambda expressions.
right have expression:
item => new myclass { somevalue = item.name, // generated other code anothervalue = item.someothercolumn, // there can lots of these aliascount = p.aliases.count() // here of course problem }
obviously want replace expression "p" when building expression item (which have , know how use).
tldr there easy way replace instances of parameter usaged in lambdaexpression expression?
an example started.
class program { static void main(string[] args) { expression<func<foo1, int>> expression1 = => a.foo2s.count(); expression<func<ienumerable<foo1>, ienumerable<foo2>>> expression2 = => a.select(b => new foo2 { string1 = "asdf", foo2count = 3 }); memberassignment foo2countassignment = getexpression(expression2); // in case, constantexpression value of 3. var expression = foo2countassignment.expression constantexpression; expression<func<ienumerable<foo1>, ienumerable<foo2>>> expression3 = => a.select(b => new foo2 { string1 = "asdf", foo2count = b.foo2s.count() }); foo2countassignment = getexpression(expression3); // in case, expression<func<foo1, int>> // same expression1, except has different parameter. var expressionresult = foo2countassignment.expression methodcallexpression; var foo2spropertyexpression = expressionresult.arguments[0] memberexpression; // "b".foo2scount() var thebparameter = foo2spropertyexpression.expression parameterexpression; // practical demonstartion. var mce = expression1.body methodcallexpression; var selectstatement = expression2.body methodcallexpression; var selectlambda = selectstatement.arguments[1] expression<func<foo1, foo2>>; var bparameter = selectlambda.parameters[0]; var me = mce.arguments[0] memberexpression; var newexpression = me.update(bparameter); // go expression tree using update create new expression till first level. // unless find way replace me. } public static memberassignment getexpression(expression<func<ienumerable<foo1>, ienumerable<foo2>>> expression2) { // a."select" var selectstatement = expression2.body methodcallexpression; // a.select("b => new foo2..." var selectlambda = selectstatement.arguments[1] expression<func<foo1, foo2>>; // a.select(b => "new foo2" var newfoo2statement = selectlambda.body memberinitexpression; // a.select(b => new foo2 {string1 = "asdf", !!foo2count = 3!! }) return newfoo2statement.bindings[1] memberassignment; } } public class foo1 { public ienumerable<foo2> foo2s { get; set; } } public class foo2 { public string string1 { get; set; } public int foo2count { get; set; } }
basically program traverses expression tree , lays out each node. can use ".gettype()" on each node exact type of expression , deal them accordingly. (hardcoded , known before hand in example).
the last example demonstrates how can replace " " in a.foo2s.count() " b " can replaced second longer expression.
then ofcourse need think of way can auto detect , replicated of expression 1 backinto expression 2.
not easy task.
Comments
Post a Comment