Pass Expression Parameter as Argument to Another Expression

Pass expression parameter as argument to another expression

If I understand correctly, you want to reuse an expression tree inside another one, and still allow the compiler to do all the magic of building the expression tree for you.

This is actually possible, and I have done it in many occasions.

The trick is to wrap your reusable part in a method call, and then before applying the query, unwrap it.

First I would change the method that gets the reusable part to be a static method returning your expression (as mr100 suggested):

 public static Expression<Func<Quote,QuoteProductImage, bool>> FilterQuoteProductImagesByQuote()
{
return (q,qpi) => q.User.Id == qpi.ItemOrder;
}

Wrapping would be done with:

  public static TFunc AsQuote<TFunc>(this Expression<TFunc> exp)
{
throw new InvalidOperationException("This method is not intended to be invoked, just as a marker in Expression trees!");
}

Then unwrapping would happen in:

  public static Expression<TFunc> ResolveQuotes<TFunc>(this Expression<TFunc> exp)
{
var visitor = new ResolveQuoteVisitor();
return (Expression<TFunc>)visitor.Visit(exp);
}

Obviously the most interesting part happens in the visitor.
What you need to do, is find nodes that are method calls to your AsQuote method, and then replace the whole node with the body of your lambdaexpression. The lambda will be the first parameter of the method.

Your resolveQuote visitor would look like:

    private class ResolveQuoteVisitor : ExpressionVisitor
{
public ResolveQuoteVisitor()
{
m_asQuoteMethod = typeof(Extensions).GetMethod("AsQuote").GetGenericMethodDefinition();
}
MethodInfo m_asQuoteMethod;
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (IsAsquoteMethodCall(node))
{
// we cant handle here parameters, so just ignore them for now
return Visit(ExtractQuotedExpression(node).Body);
}
return base.VisitMethodCall(node);
}

private bool IsAsquoteMethodCall(MethodCallExpression node)
{
return node.Method.IsGenericMethod && node.Method.GetGenericMethodDefinition() == m_asQuoteMethod;
}

private LambdaExpression ExtractQuotedExpression(MethodCallExpression node)
{
var quoteExpr = node.Arguments[0];
// you know this is a method call to a static method without parameters
// you can do the easiest: compile it, and then call:
// alternatively you could call the method with reflection
// or even cache the value to the method in a static dictionary, and take the expression from there (the fastest)
// the choice is up to you. as an example, i show you here the most generic solution (the first)
return (LambdaExpression)Expression.Lambda(quoteExpr).Compile().DynamicInvoke();
}
}

Now we are already half way through. The above is enough, if you dont have any parameters on your lambda. In your case you do, so you want to actually replace the parameters of your lambda to the ones from the original expression. For this, I use the invoke expression, where I get the parameters I want to have in the lambda.

First lets create a visitor, that will replace all parameters with the expressions that you specify.

    private class MultiParamReplaceVisitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> m_replacements;
private readonly LambdaExpression m_expressionToVisit;
public MultiParamReplaceVisitor(Expression[] parameterValues, LambdaExpression expressionToVisit)
{
// do null check
if (parameterValues.Length != expressionToVisit.Parameters.Count)
throw new ArgumentException(string.Format("The paraneter values count ({0}) does not match the expression parameter count ({1})", parameterValues.Length, expressionToVisit.Parameters.Count));
m_replacements = expressionToVisit.Parameters
.Select((p, idx) => new { Idx = idx, Parameter = p })
.ToDictionary(x => x.Parameter, x => parameterValues[x.Idx]);
m_expressionToVisit = expressionToVisit;
}

protected override Expression VisitParameter(ParameterExpression node)
{
Expression replacement;
if (m_replacements.TryGetValue(node, out replacement))
return Visit(replacement);
return base.VisitParameter(node);
}

public Expression Replace()
{
return Visit(m_expressionToVisit.Body);
}
}

Now we can advance back to our ResolveQuoteVisitor, and hanlde invocations correctly:

        protected override Expression VisitInvocation(InvocationExpression node)
{
if (node.Expression.NodeType == ExpressionType.Call && IsAsquoteMethodCall((MethodCallExpression)node.Expression))
{
var targetLambda = ExtractQuotedExpression((MethodCallExpression)node.Expression);
var replaceParamsVisitor = new MultiParamReplaceVisitor(node.Arguments.ToArray(), targetLambda);
return Visit(replaceParamsVisitor.Replace());
}
return base.VisitInvocation(node);
}

This should do all the trick.
You would use it as:

  public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
Expression<Func<Quote, FilteredViewModel>> selector = q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.Where(qpi => ExpressionHelper.FilterQuoteProductImagesByQuote().AsQuote()(q, qpi)))
};
selector = selector.ResolveQuotes();
return _context.Context.Quotes.Select(selector);
}

Of course I think you can make here much more reusability, with defining expressions even on a higher levels.

You could even go one step further, and define a ResolveQuotes on the IQueryable, and just visit the IQueryable.Expression and creating a new IQUeryable using the original provider and the result expression, e.g:

    public static IQueryable<T> ResolveQuotes<T>(this IQueryable<T> query)
{
var visitor = new ResolveQuoteVisitor();
return query.Provider.CreateQuery<T>(visitor.Visit(query.Expression));
}

This way you can inline the expression tree creation. You could even go as far, as override the default query provider for ef, and resolve quotes for every executed query, but that might go too far :P

You can also see how this would translate to actually any similar reusable expression trees.

I hope this helps :)

Disclaimer: Remember never copy paste code from anywhere to production without understanding what it does. I didn't include much error handling here, to keep the code to minimum. I also didn't check the parts that use your classes if they would compile. I also don't take any responsability for the correctness of this code, but i think the explanation should be enough, to understand what is happening, and fix it if there are any issues with it.
Also remember, that this only works for cases, when you have a method call that produces the expression. I will soon write a blog post based on this answer, that allows you to use more flexibility there too :P

Passing an expression tree as a parameter to another expression tree

I think this does what you are asking for:

Expression<Func<TEntity, bool>> Combined
{
get
{
var entity = Expression.Parameter(typeof(TEntity));
var pa = Expression.Invoke(PropertyAccessor, entity);
var te = Expression.Invoke(TestExpression, pa);
return (Expression<Func<TEntity, bool>>) Expression.Lambda(te, entity);
}
}

I tested this and it works as I would expect.

However, re-reading your original question (before my edits), I am beginning to get the impression that you asked the wrong question and that you probably don’t need expression trees. If all you need is functions, then you can use them without Expression:

private Func<TEntity, TPropertyResult> PropertyAccessor { get; set; }
private Func<TPropertyResult, bool> TestExpression { get; set; }
private Func<TEntity, bool> Combined
{
get
{
return entity => TestExpression(PropertyAccessor(entity));
}
}

Example of use:

// Set up the original functions
PropertyAccessor = entity => GenerateResult(entity);
TestExpression = result => result.IsCool();

// This stores a reference to the combined function
var fn = Combined;

// This actually evaluates the function
bool isCool = fn(myEntity);

// Alternatively, you could evaluate the function directly, without the variable
bool isCool = Combined(myEntity);

Expression.MethodCallExpression pass MemberExpression as Parameter

It depends on the Type of memberExpression which in turn depends on the type that the property accessed has.

E.g. the following works:

void Main()
{
string propertyName = "ObjectProperty";
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
MemberExpression memberExpression = Expression.Property(parameterExpression, propertyName);
MethodCallExpression methodCall = Expression.Call(
typeof(Enumerable),
"Contains",
new Type[] { typeof(object) },
Expression.Constant(new Object[] { 1, 2, 3 }),
memberExpression
);

Console.WriteLine(Expression.Lambda<Func<T, bool>>(methodCall, parameterExpression).Compile()(new T()));
}

public class T
{
public object ObjectProperty => 2;
public int IntProperty => 4;
}

The following does not:

void Main()
{
string propertyName = "IntProperty";
ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
MemberExpression memberExpression = Expression.Property(parameterExpression, propertyName);
MethodCallExpression methodCall = Expression.Call(
typeof(Enumerable),
"Contains",
new Type[] { typeof(object) },
Expression.Constant(new Object[] { 1, 2, 3 }),
memberExpression
);

Console.WriteLine(Expression.Lambda<Func<T, bool>>(methodCall, parameterExpression).Compile()(new T()));
}

public class T
{
public object ObjectProperty => 2;
public int IntProperty => 4;
}

You could use new Type[] { memberExpression.Type } instead of new Type[] { typeof(object) } to have the code adapt to the type of the property, though you'd also need to have the type of the argument expression (in this case the Constant(new Object[] {...})) match.

Note that you can do implicit casts here only if they are reference types that derive from the type in question (object) so a property that returned string or Uri would be fine (though obviously always false in the check for whether it was contained in an array of 1, 2, 3) but a property that returned int is not as it's a boxing conversion, not a reference up-cast.

How to pass EL argument in method?

You can indeed not nest EL expressions this way. EL expressions can only be inlined.

You can use <c:set> to create a new variable wherein the desired expression is inlined in the desired value and then reuse this variable as argument of another EL expression.

xmlns:c="http://java.sun.com/jsp/jstl/core"
...
<c:set var="filterGroup" value="[tb:store_sgroup][fd:title]=#{store_itemController.filter_group}" scope="request" />
...
<f:selectItems value="#{commonDataFunctions.getItemByName('store_sgroup', 'id', 'title', filterGroup, '[tb:store_sgroup][fd:title]', true)}"/>

How to pass an expression as an argument/parameter?

You want a function pointer.

Make the expression a function.

float fexpression( float x )
{
return (5*(pow(x,4))) + (3*(pow(x, 3))) + (10 * x) - 5
}

This code, as you wrote it, will then pass the functionality itself.

   if (/* gexpression == true */)
cout << gfunction(fexpression);
else /* h expression == true */
cout << hfunction(fexpression);

How to pass parameter in value expression and method expression in JSF 2?

You don't need to pass a parameter in the example you've provided.

In this situation getters and setters are invoked automatically on your backing bean.

The following code will invoke getUserNumber and/or setUserNumber to retrieve and/or modify the value of the inputText component:

<h:inputText value="#{userNumberBean.userNumber}">

The form value entered by the user will be passed to setUserNumber as a parameter.


To pass a parameter to a backing bean method, you might do something like this:

<h:commandButton  action="#{userNumberBean.displayAlert('Hey')}"    value="Say Hey"/>
<h:commandButton action="#{userNumberBean.displayAlert('Later')}" value="Say Bye"/>

This would invoke a method that looks like this:

public String displayAlert(String someText)


As Bhesh Gurung's answer suggests, you can set userNumber to 5 by default in the constructor.

You could also use one of the methods suggested here to apply a default value.

Is there a way to instantiate a class via Expression.New where 'ConstructorInfo' is supplied by an ExpressionParameter?

So my question: is there a way around this with Expression.New? I know I can use ConstructorInfo.Invoke via another Expression.Call.

This is exactly what you should do.

Think about the expression tree that you want to create. Since the final usage that you want to achieve looks like:

func.Invoke(typeof(ClassToBeInstantiated), 4);

func's expression must look like:

(type, count) => GetConstructor.GetNonGenericConstructorInfo(type, typeof(int)).Invoke(new object[] {count})

This seems to be what you were trying to do too, just with an extra local variable.

It cannot be:

(type, count) => new type(count)

Because new type(count) is not a valid expression. type is just a parameter.

This has nothing to do with new expressions that Expression.New creates. There are no NewExpressions in your desired expression at all. It would be very weird if Expression.New suddenly starts inserting ConstructorInfo.Invoke calls in the expression tree, because that is not what it is supposed to create at all.

You could use Expression.New here to create expressions such as:

(type, count) => new ClassToBeInstantiated(count)

But I don't think that's what you want... You want the type parameter to determine what type to instantiate, and assume that a one-parameter int constructor to be available, right?

Using sharplab.io, you can write the lambda expression you want, and see what code the compiler actually generates for building the expression tree. After cleaning up the code that generates for

Expression<Func<Type, int, object>> func =
(type, count) => GetConstructor.GetNonGenericConstructorInfo(type, typeof(int)).Invoke(new object[] {count});

You get:

MethodInfo getConstructorMethod = typeof(GetConstructor).GetMethod(nameof(GetConstructor.GetNonGenericConstructorInfo));
MethodInfo invokeMethod = typeof(ConstructorInfo).GetMethod(nameof(ConstructorInfo.Invoke), new Type[] { typeof(object[]) });

ParameterExpression typeParam = Expression.Parameter(typeof(Type), "type");
ParameterExpression countParam = Expression.Parameter(typeof(int), "count");

// GetNonGenericConstructorInfo(type, typeof(int))
MethodCallExpression instance = Expression.Call(null, getConstructorMethod,
typeParam, Expression.NewArrayInit(typeof(Type), Expression.Constant(typeof(int), typeof(Type)))
);
// // .Invoke(new object[] { count })
MethodCallExpression body = Expression.Call(instance, invokeMethod,
Expression.NewArrayInit(typeof(object), Expression.Convert(countParam, typeof(object)))
);
Expression<Func<Type, int, object>> func = Expression.Lambda<Func<Type, int, object>>(body, typeParam, countParam);

And func.Compile()(typeof(ClassToBeInstantiated), 4) as ClassToBeInstantiated is not null.

How to pass runtime argument variable in Expression.Call?

I solved it like this (shown here Calling (params object[]) with Expression[]):

//I have other overloads for M, hence I need to specify the type of arugments
var methodInfo = typeof(C).GetMethod("M", new Type[] { typeof(Type), typeof(int[]) });

//I fixed this issue where the first argument should be typeof(Type)
var typeArgumentExp = Expression.Parameter(typeof(Type));

var intArrayArgumentExp = Expression.NewArrayInit(typeof(int), Enumerable.Repeat(Expression.Constant(0), 3));

var combinedArgumentsExp = new Expression[] { typeArgumentExp }.Concat(intArrayArgumentExp);
var call = Expression.Call(methodInfo, combinedArgumentsExp);

Expression.NewArrayInit does the trick. Thanks hvd for his direction.



Related Topics



Leave a reply



Submit