score:1
for both of your examples this can actually be done with two expression visitors (code is commented):
static class extensions {
public static tresult fakeinvoke<tresult>(this delegate instance, params object[] parameters)
{
// this is not intended to be called directly
throw new notimplementedexception();
}
public static texpression unwrap<texpression>(this texpression exp) where texpression : expression {
return (texpression) new fakeinvokevisitor().visit(exp);
}
class fakeinvokevisitor : expressionvisitor {
protected override expression visitmethodcall(methodcallexpression node) {
// replace fakeinvoke call
if (node.method.name == "fakeinvoke") {
// first obtain reference to method being called (so, for c.fakeinvoke(...) that will be "c")
var func = (delegate)expression.lambda(node.arguments[0]).compile().dynamicinvoke();
// explore method argument names and types
var argumentnames = new list<string>();
var dummyarguments = new list<object>();
foreach (var arg in func.method.getparameters()) {
argumentnames.add(arg.name);
// create default value for each argument
dummyarguments.add(arg.parametertype.isvaluetype ? activator.createinstance(arg.parametertype) : null);
}
// now, invoke function with default arguments to obtain expression (for example, this one () => a*(a + 3)).
// all arguments will have default value (0 in this case), but they are not literal "0" but a reference to "a" member with value 0
var exp = (expression) func.dynamicinvoke(dummyarguments.toarray());
// this is expressions representing what we passed to fakeinvoke (for example expression (x + 3))
var argumentexpressions = (newarrayexpression)node.arguments[1];
// now invoke second visitor
exp = new innerfakeinvokevisitor(argumentexpressions, argumentnames.toarray()).visit(exp);
return ((lambdaexpression)exp).body;
}
return base.visitmethodcall(node);
}
}
class innerfakeinvokevisitor : expressionvisitor {
private readonly newarrayexpression _args;
private readonly string[] _argumentnames;
public innerfakeinvokevisitor(newarrayexpression args, string[] argumentnames) {
_args = args;
_argumentnames = argumentnames;
}
protected override expression visitmember(memberexpression node) {
// if that is a reference to one of our arguments (for example, reference to "a")
if (_argumentnames.contains(node.member.name)) {
// find related expression
var idx = array.indexof(_argumentnames, node.member.name);
var argument = _args.expressions[idx];
var unary = argument as unaryexpression;
// and replace it. so "a" is replaced with expression "x + 3"
return unary?.operand ?? argument;
}
return base.visitmember(node);
}
}
}
can be used like this:
func<int, expression<func<int>>> c = (int a) => () => a * (a + 3);
expression<func<int, int>> d = (x) => 2 + c.fakeinvoke<int>(3 + x);
d = d.unwrap(); // this is now "x => (2 + ((3 + x) * ((3 + x) + 3)))"
simple case:
func<int, expression<func<int>>> c = (int a) => () => a + 3;
expression<func<int>> d = () => 2 + c.fakeinvoke<int>(3);
d = d.unwrap(); // this is now "() => 2 + (3 + 3)
with multiple arguments:
func<int, int, expression<func<int>>> c = (int a, int b) => () => a * (a + 3) + b;
expression<func<int, int>> d = (x) => 2 + c.fakeinvoke<int>(3 + x, x + 5);
d = d.unwrap(); // "x => (2 + (((3 + x) * ((3 + x) + 3)) + (x + 5)))"
note that fakeinvoke is not type-safe (you should explicitly set return type and arguments and not checked). but that's just for example, in real use you can create many overloads of fakeinvoke, like this:
public static tresult fakeinvoke<targ, tresult>(this func<targ, expression<func<tresult>>> instance, targ argument) {
// this is not intended to be called directly
throw new notimplementedexception();
}
code above should be modified a bit to handle such calls correctly (because arguments are now not in single newarrayexpression), but that's quite easy to do. with such overloads you can just do:
expression<func<int, int>> d = (x) => 2 + c.fakeinvoke(3 + x); // this is type-safe now, you cannot pass non-integer as "3+x", nor you can pass more or less arguments than required.
score:-1
using linqkit, it simple to consume it's expandable query wrapper by calling asexpandable()
on the first entity type. this expandable wrapper does the work necessary to compose expressions make them compatible with ef.
a toy example of it's usage is below (person
is an ef code first entity) -
var ctx = new test();
expression<func<person, bool>> agefilter = p => p.age < 30;
var filtered = ctx.people.asexpandable()
.where(p => agefilter.invoke(p) && p.name.startswith("j"));
console.writeline( $"{filtered.count()} people meet the criteria." );
score:0
the case where expressions are returned from lambdas is really hard because those expressions are actually closures that have non-public (system.runtime.compilerservices.closure
?) object inside them that contains the values that lambda closes over. all of that makes really hard to accurately replace formal parameters with actual parameters in expression tree.
inspired by evk response i found fairly elegant solution for simpler case:
expression<func<int, int>> c = (int a) => a * (a + 3);
var d = extensions.splice<func<int, int>>((x) => 2 + c.embed(3 + x));
// d is now x => (2 + ((3 + x) * ((3 + x) + 3))) expression
public static class extensions
{
public static t embed<t>(this expression<func<t>> exp) { throw new exception("should not be executed"); }
public static t embed<a, t>(this expression<func<a, t>> exp, a a) { throw new exception("should not be executed"); }
public static t embed<a, b, t>(this expression<func<a, b, t>> exp, a a, b b) { throw new exception("should not be executed"); }
public static t embed<a, b, c, t>(this expression<func<a, b, c, t>> exp, a a, b b, c c) { throw new exception("should not be executed"); }
public static t embed<a, b, c, d, t>(this expression<func<a, b, c, d, t>> exp, a a, b b, c c) { throw new exception("should not be executed"); }
public static expression<t> splice<t>(expression<t> exp)
{
return new splicingvisitor().visit(exp) as expression<t>;
}
class splicingvisitor : expressionvisitor
{
protected override expression visitmethodcall(methodcallexpression node)
{
if (node.method.name == "embed")
{
var mem = node.arguments[0] as memberexpression;
var getterlambda = expression.lambda<func<object>>(mem, new parameterexpression[0]);
var lam = getterlambda.compile().dynamicinvoke() as lambdaexpression;
var parametermapping = lam.parameters.select((p, index) => new
{
formalparameter = p,
actualparameter = node.arguments[index+1]
}).todictionary(o => o.formalparameter, o => o.actualparameter);
return new parameterreplacervisitor(parametermapping).visit(lam.body);
}
return base.visitmethodcall(node);
}
}
public class parameterreplacervisitor : expressionvisitor
{
private dictionary<parameterexpression, expression> parametermapping;
public parameterreplacervisitor(dictionary<parameterexpression, expression> parametermapping)
{
this.parametermapping = parametermapping;
}
protected override expression visitparameter(parameterexpression node)
{
if(parametermapping.containskey(node))
{
return parametermapping[node];
}
return base.visitparameter(node);
}
}
}
Source: stackoverflow.com
Related Query
- How to 'unquote' when creating Expression tree from lambda?
- How Build Lambda Expression Tree with multiple conditions
- Creating lambda expression from a string
- How to reuse a linq expression for 'Where' when using multiple source tables
- Access property in lambda expression from string when using LINQ
- How to get unique string from a lambda expression
- Combine property selector expression tree and value to create a predicate for EF filtering - create filter from lambda selector and value
- How to get leaf node from a nested collection using lambda expression
- Creating a string from a lambda expression
- creating Linq to sqlite dbml from DbLinq source code
- How to extract a where clause expression tree from IQueryable
- How to create dynamic lambda based Linq expression from a string in C#?
- How to extract max(property) from left join in LINQ with lambda expression
- Lambda Expression - How to provide values to where clause from an IEnumerable<Object>?
- How to write the same code using Lambda Expression
- How to write following code in lambda expression or linq?
- how can i return model property from lambda expression (like mvc's "html.textboxfor(yyy)")?
- How to use a lambda expression to return a field from two collections
- How to return multiple rows using LINQ lambda expression from SQL Server 2014?
- How to build expression tree from string values
- How to select Item from an property list using Expression Tree in c#
- How much of a performane hit will i take from casting when trying to make this code mistake proof?
- How could I extract the member path from this lambda expression
- Creating dynamic Lambda from Existing Lambda Expression
- How to write a Lambda Expression to select distinct words from list of sentences
- How to get the value of a field from Lambda expression for ForEach()?
- How to cast into a list from a Linq lambda expression
- How to use a variable when making a lambda expression using Expression.Lambda<Func<>>()
- Expression tree works when manual input, but not coming from another class
- How to get an Expression from a Lambda Expression
More Query from same tag
- group table by foreign key table in mvc5 using linq
- Order by long Distance from Google Distance Matrix
- Filter Related Tables in LINQ
- Linq Matching Pattern
- TakeWhile, but get the element that stopped it also
- Translate SQL 'not in' into LINQ to Entities using Entity Framework 4
- ASP.NET: Store user information on submit
- Union collections only if second TSource is not Null
- I need to find max total of the sum of integers in Generic list using Linq
- Saving the result of a linq query to an XML file
- remove duplicate data in custom structure with LINQ
- Entity framework and datetime compare
- How to perform a case-sensitive LINQ query in Azure?
- LINQ to Entities string to decimal a column
- Select first element that has certain property if there are any duplicates otherwise select first element
- Nested classes query and join
- How to create Expression<Func<object>> from property name
- using LINQ where(lamdaexp).First and First(lambdaexp)
- export to excel give me checkbox instead of string
- How to store a Linq query and use it later?
- Looping LINQ query using do while and display the result
- Update all cells of a datatable in one go in c#
- Linq querying local collections against database
- Construct a LINQ query to return an item from a List that is related by one property to other members but which differs by a second property
- LINQ to Dictionary with AND condition
- Search if exist before insert in Linq DB in C# for WP8
- Add carriage return to a string
- Can you reverse order a string in one line with LINQ or a LAMBDA expression
- Find string with most frequency of a character in List of strings using LINQ C#
- Find all parents in hierarchical SQL database