Reputation: 23103
I have two expression trees defined like this:
private Expression<Func<TEntity, TPropertyResult>> PropertyAccessor { get; set; }
and
private Expression<Func<TPropertyResult, bool>> TestExpression { get; set; }
I need to create a new expression tree that will result in the equivalent of:
var expression = p => this.TestExpression(this.PropertyAccessor(p));
When using Expression.Invoke(this.TestExpression, this.PropertyAccessor)
, I get the following error
{"Expression of type 'System.Func`2[MyEntity,System.String]' cannot be used for parameter of type 'System.String'"}
TPropertyResult
is a string during my test.
I tried using Expression.Call
or Expression.Invoke
. No luck. What should I use?
Upvotes: 9
Views: 3045
Reputation: 1107
The easiest way I found to do this is by using LinqKit (https://github.com/scottksmith95/LINQKit)
With it you can actually do
var expression = p => this.TestExpression.Invoke(this.PropertyAccessor(p));
db.Users.Where(expression.Expand());
Expand comes with LinqKit and does the magic here, it allows EF to be able to do the translation to SQL despite having the Invoke in your expression.
Upvotes: 0
Reputation: 66604
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);
Upvotes: 9