I'm using the ExpressionVisitor class to dynamically alter search queries sent to a database. I want to replace a binary Or expression, which is the left operand of an AndAlso expression, with a Contains method but my present code is generating the following error:
The binary operator AndAlso is not defined for the types 'System.Func`2[foo.MV,System.Boolean]' and 'System.Boolean'.
Expression AndAlso documentation suggests that I should be able to provide (Expression, Expression) as arguments to this Binary AndAlso Expression but the code below apparently does not satisfy that requirement for the left operand:
protected override Expression VisitBinary(BinaryExpression b) {
if (b.NodeType == ExpressionType.Or) {
ParameterExpression vParam = Expression.Parameter(typeof(MV), "mv");
var mvID = Expression.Property(vParam, "MVID");
ConstantExpression ftsIDs = Expression.Constant(FtsIds, typeof(List<long>));
var containsInfo = typeof(List<long>).GetMethod("Contains", new Type[] { typeof(long) });
var containsExp = Expression.Call(ftsIDs, containsInfo, new Expression[] { mvID });
return Expression.Lambda<Func<MV, bool>>(containsExp, vParam);
}
}
I understand the basic message: the types don't match but I can't see what more I need to do to convert my replacement code to something that works and all the example code I've looked through have scenarios that are too simple.
Update:
Thanks to Antonin. Keywords: "Outside the epxression". vParam is already defined.
The above code is recreating it and the compiler will likely give it a funky name.... and ultimately be a referencing a different instance. What actually needs to be done is harvest the existing vParam expression as below:
protected override Expression VisitBinary(BinaryExpression b) {
if (b.NodeType == ExpressionType.Or) {
MethodCallExpression mce = this.Visit(b.Left) as MethodCallExpression;
MemberExpression mex = mce?.Object as MemberExpression;
var mvID = Expression.Property(mex.Expression, "MVID"); // << Injecting existing param here
ConstantExpression ftsIDs = Expression.Constant(FtsIds, typeof(List<long>));
var containsInfo = typeof(List<long>).GetMethod("Contains", new Type[] { typeof(long) });
var containsExp = Expression.Call(ftsIDs, containsInfo, new Expression[] { mvID });
return containsExp;
}
}
As mex.Expression contains the reference to the original ParameterExpression that the original code was essentially recreating via vParam. So much easier when you look down instead of up ;)