I know I can write the following to generate lambda expression:
Expression<Func<string, bool>> lambda = s => s.Length == 5;
But is there any way to automatically generate expression for property? In other words is there strongly-typed analogue of this:
var property = Expression.Property("Name")
This will give you a lambda which returns the Length property:
Expression<Func<string, int>> lambda = s => s.Length;
If you don't want the full lambda, but only the MemberExpression which accesses the property, you can do that:
var propertyExpression = (MemberExpression)lambda.Body;
Expressions<Func<ClassWithProperty, PropertyReturnType>> lambda = C => C.Name;
Related
I want to dynamically build a LINQ query so I can do something like
var list = n.Elements().Where(getQuery("a", "b"));
instead of
var list = n.Elements().Where(e => e.Name = new "a" || e.Name == "c");
(Most of the time, I need to pass XNames with namespaces, not just localnames...)
My problem is in accessing the array elements:
private static Func<XElement, bool> getQuery(XName[] names)
{
var param = Expression.Parameter(typeof(XElement), "e");
Expression exp = Expression.Constant(false);
for (int i = 0; i < names.Length; i++)
{
Expression eq = Expression.Equal(
Expression.Property(param, typeof(XElement).GetProperty("Name")!.Name),
/*--->*/ Expression.Variable(names[i].GetType(), "names[i]")
);
}
var lambda = Expression.Lambda<Func<XElement, bool>>(exp, param);
return lambda.Compile();
}
Obviously the Variable expression is wrong, but I'm having difficulty building an expression capable of accessing the array values.
Do you need to create an expression and compile it? Unless I'm missing some nuance to this, all you need is a function that returns a Func<XElement, bool>.
private Func<XElement, bool> GetQuery(params string[] names)
{
return element => names.Any(n => element.Name == n);
}
This takes an array of strings and returns a Func<XElement>. That function returns true if the element name matches any of the arguments.
You can then use that as you described:
var list = n.Elements.Where(GetQuery("a", "b"));
There are plenty of ways to do something like this. For increased readability an extension like this might be better:
public static class XElementExtensions
{
public static IEnumerable<XElement> WhereNamesMatch(
this IEnumerable<XElement> elements,
params string[] names)
{
return elements.Where(element =>
names.Any(n => element.Name == n));
}
}
Then the code that uses it becomes
var list = n.Elements.WhereNamesMatch("a", "b");
That's especially helpful when we have other filters in our LINQ query. All the Where and other methods can become hard to read. But if we isolate them into their own functions with clear names then the usage is easier to read, and we can re-use the extension in different queries.
If you want to write it as Expression you can do it like so:
public static Expression<Func<Person, bool>> GetQuery(Person[] names)
{
var parameter = Expression.Parameter(typeof(Person), "e");
var propertyInfo = typeof(Person).GetProperty("Name");
var expression = names.Aggregate(
(Expression)Expression.Constant(false),
(acc, next) => Expression.MakeBinary(
ExpressionType.Or,
acc,
Expression.Equal(
Expression.Constant(propertyInfo.GetValue(next)),
Expression.Property(parameter, propertyInfo))));
return Expression.Lambda<Func<Person, bool>>(expression, parameter);
}
Whether or not you compile the expression is determined by the means you want to achieve. If you want to pass the expression to a query provider (cf. Queryable.Where) and have e.g. the database filter your values, then you may not compile the expression.
If you want to filter a collection in memory, i.e. you enumerate all elements (cf. Enumerable.Where) and apply the predicate to all the elements, then you have to compile the expression. In this case you should probably not use the Expression api as this adds complexity to your code and you are then more vulnerable to runtime errors.
I'm stuck on a lambda with a single int parameter and a bool return value:
Expression<Func<int, bool>> myFunc = x => x == 5;
First, I tried this that returns a new Func that I can't make sense of; I was expecting a true boolean value:
var boolResult = Expression.Lambda(myFunc).Compile().DynamicInvoke(5);
Then I tried to explictly set the function parameters and return type instead:
var param = Expression.Parameter(typeof(int), "x");
var fn = Expression.Lambda<Func<int, bool>> (myFunc, param).Compile();
, but this throws an error:
System.ArgumentException : Expression of type
'System.Func`2[System.Int32,System.Boolean]' cannot be used for return
type 'System.Boolean'
Which is weird, but I tried to convert the expression:
var fn = Expression.Lambda<Func<int, bool>> (
Expression.Convert(myFunc,
typeof(Func<int, bool>))
, param).Compile();
var boolResult = fn.Invoke(5);
, this however did not help and gives the same error:
System.ArgumentException : Expression of type
'System.Func`2[System.Int32,System.Boolean]' cannot be used for return
type 'System.Boolean'
Any idea of what I'm doing wrong here?
The error in your dynamic invocation code is the way in which you construct your lambda. When you pass myFunc as expression, you get a lambda that returns Func<int,bool>.
If you want to make a new LambdaExpression that takes int and return bool, you could harvest Body and Parameters of your myFunc object, like this:
var b = Expression.Lambda(myFunc.Body, myFunc.Parameters).Compile().DynamicInvoke(5);
or, since myFunc is already a LambdaExpression, you could compile it directly:
var c = myFunc.Compile().DynamicInvoke(6);
Demo.
I have an Expression which could be any value. For example it could be () => 5 + 5. How would I use that Expression to search a List<int> to return true if the value is found in the List<int>? What I'm trying to do is I'm given an Expression and I'm checking a list to see if the value of the Expression is in the list
private int FindRecord(System.Linq.Expressions.Expression expression)
{
// expression when stepping through in debug is {() => 5 + 5}
List<int> list = new List<int>();
list.Add(5);
list.Add(10);
return list.Any(expression); // syntax issue - cannot convert from Expression to Func<int,bool>
}
EDIT : I misunderstood the question so here is my new answer:
private bool FindRecord(System.Linq.Expressions.Expression<Func<int>> expression)
{
int valueToSearch = expression.Compile()();
List<int> list = new List<int>();
list.Add(5);
list.Add(10);
return list.Any(i => i == valueToSearch); // syntax issue - cannot convert from Expression to Func<int,bool>
}
The return type of you function should be a "bool" (Any return a bool, not an int).
Parameter type "Expression" is not accurate enough, you should say it's a expression of a function return an integer.
You need to compile this expression to be able to "run" it and to get the actual value.
Well, you can do it as follows without Compile() on the Expression parameter, but it's really not a good idea. You generally don't want to be passing Expression around in your code as it will fail at runtime instead of compile time. You should change your method to accept an Expression of the expected type instead, or make it generic.
private static bool FindRecord(Expression expression, IEnumerable<int> list)
{
ParameterExpression parameter = Expression.Parameter(typeof(int), "x");
Expression body = Expression.Equal(parameter, Expression.Invoke(expression));
Expression<Func<int, bool>> lambda = (Expression<Func<int, bool>>)Expression.Lambda(body, parameter);
Console.WriteLine(lambda);
return list.Any(lambda.Compile());
}
Given:
var paramA = Expression.Parameter(typeof(string), "a");
and:
Expression<Func<string, bool>> expr = b => b == "Something";
Is there anyway to replace b by paramA into the expression expr?
You can walk the expression tree of expr, and replace all occurrences of b with paramA using the approach described in this Q&A: "Combine two lambda expressions with inner expression".
However, if you simply need a lambda expression that uses paramA as its parameter, it is easier to make a lambda that wraps expr instead:
var res = (Expression<Func<string,bool>>)Expression.Lambda(
Expression.Invoke(expr, paramA)
, paramA
);
I am learning expression trees in C#.
I am stuck now for a while:
string filterString = "ruby";
Expression<Func<string, bool>> expression = x => x == filterString;
How can I construct this expression by code? There is no sample how to capture a local variable. This one is easy:
Expression<Func<string, bool>> expression = x => x == "ruby";
This would be:
ParameterExpression stringParam = Expression.Parameter(typeof(string), "x");
Expression constant = Expression.Constant("ruby");
BinaryExpression equals = Expression.Equal(stringParam, constant);
Expression<Func<string, bool>> lambda1 =
Expression.Lambda<Func<string, bool>>(
equals,
new ParameterExpression[] { stringParam });
The debugger prints the following for (x => x == filterString) :
{x => (x ==
value(Predicate.Program+<>c__DisplayClass3).filterString)}
Thanks for shedding some light on this topic.
Capturing a local variable is actually performed by "hoisting" the local variable into an instance variable of a compiler-generated class. The C# compiler creates a new instance of the extra class at the appropriate time, and changes any access to the local variable into an access of the instance variable in the relevant instance.
So the expression tree then needs to be a field access within the instance - and the instance itself is provided via a ConstantExpression.
The simplest approach for working how to create expression trees is usually to create something similar in a lambda expression, then look at the generated code in Reflector, turning the optimization level down so that Reflector doesn't convert it back to lambda expressions.
This code wraps the expression in a closure Block that treats the local variable as a constant.
string filterString = "ruby";
var filterStringParam = Expression.Parameter(typeof(string), "filterString");
var stringParam = Expression.Parameter(typeof(string), "x");
var block = Expression.Block(
// Add a local variable.
new[] { filterStringParam },
// Assign a constant to the local variable: filterStringParam = filterString
Expression.Assign(filterStringParam, Expression.Constant(filterString, typeof(string))),
// Compare the parameter to the local variable
Expression.Equal(stringParam, filterStringParam));
var x = Expression.Lambda<Func<string, bool>>(block, stringParam).Compile();
An old question but I came to it when trying to do something similar building expressions for Linq-to-entities (L2E) In that case you cannot use Expression.Block as it cannot be parsed down to SQL.
Here is an explicit example following Jon's answer which would work with L2E. Create a helper class to contain the value of the filter:
class ExpressionScopedVariables
{
public String Value;
}
Build the tree thus:
var scope = new ExpressionScopedVariables { Value = filterString};
var filterStringExp = Expression.Constant(scope);
var getVariable = typeof(ExpressionScopedVariables).GetMember("Value")[0];
var access = Expression.MakeMemberAccess(filterStringExp, getVariable);
And then replace the constant in the original code with the member access expression:
BinaryExpression equals = Expression.Equal(stringParam, access);
Expression<Func<string, bool>> lambda1 =
Expression.Lambda<Func<string, bool>>(
equals,
new ParameterExpression[] { stringParam });