Suppose I have something like
Expression<Func<SomeType, DateTime>> left = x => x.SomeDateProperty;
Expression<Func<SomeType, DateTime>> right = x => dateTimeConstant;
var binaryExpression = Expression.GreaterThan(left, right);
Expression<Func<SomeType, bool>> predicate =
x => x.SomeDateProperty> dateTimeConstant;
1) How can I replace the right hand of the assignment of the last line with something that uses the binaryExpression
instead? var predicate = x => binaryExpression;
doesn't work.
2) The right
is always a constant, not necessarily DateTime.Now. Could it be of some simpler Expression
type? For instance, it doesn't depend on SomeType, it is just a constant.
3) If I have the GreaterThan
as a string
, is there a way to get from this string to the method with the same name in Expression
? In general, if the name of the comparison method is given as a string
, how can I go from the string to actually calling the method with the same name on the Expression
class?
It has to work with LINQ to Entities, if it matters.
1 and 2: You need to build the expression tree manually to do this, the compiler cannot help because it only constructs ready-made expressions that represent functions in their entirety. That's not useful when you want to build functions piece by piece.
Here's one straightforward way to build the expression you want:
var argument = Expression.Parameter(typeof(SomeType));
var left = Expression.Property(argument, "SomeDateProperty");
var right = Expression.Constant(DateTime.Now);
var predicate = Expression.Lambda<Func<SomeType, bool>>(
Expression.GreaterThan(left, right),
new[] { argument }
);
You can take this for a test drive with
var param = new SomeType {
SomeDateProperty = DateTime.Now.Add(TimeSpan.FromHours(-1))
};
Console.WriteLine(predicate.Compile()(param)); // "False"
3: Since in all likelihood the number of possible choices for your binary predicate will be quite small, you could do this with a dictionary:
var wordToExpression =
new Dictionary<string, Func<Expression, Expression, BinaryExpression>>
{
{ "GreaterThan", Expression.GreaterThan },
// etc
};
Then, instead of hardcoding Expression.GreaterThan
in the first snippet you would do something like wordToExpression["GreaterThan"](left, right)
.
Of course this can also be done the standard way with reflection.
When you use GreaterThan
, you need to specify the expression bodies, not the lambda itself. Unfortunately, there is a complication: the x
in the two expressions is not the same.
In this particular case, you could just about get away with this, because the second expression does not use x
So; your "1" and "2" should be answered by:
var binaryExpression = Expression.GreaterThan(left.Body, right.Body);
var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression,
left.Parameters);
However, to handle this in the general case, you must rewrite on of the expressions, to fix-up the parameters:
var binaryExpression = Expression.GreaterThan(left.Body,
new SwapVisitor(right.Parameters[0], left.Parameters[0]).Visit(right.Body));
var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression,
left.Parameters);
with:
public class SwapVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
For your "3"; there is nothing inbuilt for that; you could use reflection, though:
string method = "GreaterThan";
var op = typeof(Expression).GetMethod(method,
BindingFlags.Public | BindingFlags.Static,
null, new[] { typeof(Expression), typeof(Expression) }, null);
var rightBody = new SwapVisitor(right.Parameters[0],
left.Parameters[0]).Visit(right.Body);
var exp = (Expression)op.Invoke(null, new object[] { left.Body, rightBody });
var lambda = Expression.Lambda<Func<SomeType, bool>>(exp, left.Parameters);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With