Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you make a System.Linq.Expressions.Expression object containing a call to Any()

I want to dynamically generate a linq.expressions.expression statement which I can use as a filter.

Here is a sample Linq query that I'd like to convert to an Expression:

ctx.customer.where(c=>ctx.Invoice.Any(i=>i.customerId == c.id));

Here's my attempt

using System.Linq.Expressions;
var c = Expression.parameter(typeof(Customer),"c");
var i = Expression.parameter(typeof(Invoice),"i");

var rightPart= Expression.Equal(
 Expression.propertyorField(i,"customerId"), Expression.propertyorfield(c,"id")

Please assist.

like image 323
Pit Ming Avatar asked Dec 04 '22 17:12

Pit Ming


2 Answers

When I need to manually create linq expression I just make .Net create for me such expression from lambda and then I can explore its structure. For example run under debug

Expression<Func<TestObject, bool>> expression = t => t.Invoice.Any(i => i.CustomerId == t.Id);

and inspect expression variable.

like image 138
Nikolay Avatar answered Dec 10 '22 10:12

Nikolay


I assume that LinqExpression comes from using LinqExpression = System.Linq.Expressions.Expression;.

There is no specific expression type for Any, because it is not an operator or the like. Any is a static method, so you should create a Call expression for that.

You will need to build the expression based on the static method syntax rather than the extension method syntax:

ctx.customer.Where(c => Enumerable.Any(ctx.Invoice, i => i.customerId == c.id));

There are a lot of steps to this. Here it is in outline, because I don't have time to work out all of the steps correctly. I'm not entirely sure how to represent the fact that the c parameter used in the inner lambda is not a parameter of that lambda, but rather of the outer lambda. In outline, you'll need to

  1. Retrieve the MethodInfo for the correct overload of the generic Enumerable.Any method.
  2. Construct the non-generic MethodInfo from the generic one (i.e., call MakeGenericMethod).
  3. Construct the PropertyExpression that you will pass as the first argument to the method (representing ctx.Invoce)
  4. Construct the body of the lambda expression that you will pass as the second argument to the method (i.e., rightPart in your example code).
  5. Construct the LambdaExpression (e.g., var innerLambda = Expression.Lambda(rightPart, i);)
  6. Construct the MethodCallExpression representing the call to the method, passing the MethodInfo and innerLambda.
  7. Construct the LambdaExpression that you will pass to Where (i.e., Expression.Lambda(methodCallExpression, c).

The fourth step will vary if you want to combine boolean expressions as indicated in your comment.

I hope that helps.

like image 26
phoog Avatar answered Dec 10 '22 11:12

phoog