C# Generate Expression<Func<Foo, object>> - c#

I am using a library containing a function which takes an expression parameter, x => x.Name.
Using reflection, I am trying to call this for each property of Foo. This means that I need to create an expression of type Expression<Func<Foo, object>> for each property.
I have been reading about expression trees, but nothing that covers this case.
foreach (var property in typeof(Foo).GetProperties())
{
ParameterExpression pe = Expression.Parameter(typeof(Foo), "x");
MemberExpression me = Expression.Property(pe, property.Name);
Expression<Func<Foo, object>> expression = ... // x => x.Name
Bar(expression, property.Name);
}
Solved:
foreach (var property in typeof(Foo).GetProperties())
{
ParameterExpression pe = Expression.Parameter(typeof(Foo), "x");
MemberExpression me = Expression.Property(pe, property.Name);
var expression = (Expression<Func<Foo, object>>)
Expression.Lambda(Expression.Convert(me, typeof(object)), pe);
Bar(expression, property.Name);
}

The problem is that you apparently do not know the type Foo at compile-time, otherwise you could simply create a lambda expression using the generic Expression.Lambda overload and be fine.
There is a overload Expression.Lambda that will create a LambdaExpression and derives the type parameters from the expression you specified. This means, it will deduct it from the parameter expression and the member expression you use as body.
In both implementations, there is a catch, though. The return type object is a reference type. The properties may however return a value type (such as int), which requires casting. Normally, the compiler does that for you. In this case, you have to do that by yourself.
private IEnumerable<LambdaExpression> CreateExpressions(Type fooType)
{
foreach (var property in fooType.GetProperties())
{
ParameterExpression pe = Expression.Parameter(fooType, "x");
MemberExpression me = Expression.Property(pe, property.Name);
yield return Expression.Lambda(Expression.Convert(me, typeef(object)), pe);
}
}
If you know the type Foo at compile-time, you can simply add the generic parameter Func<Foo, object> to the Lambda method.

Related

Lambda type parameter must be derived from System.Delegate

In a C# project, I want to create an extension that would take a list of property names and create a dynamic select query.
I found magic Gist that seems to be doing exactly that. However, var lambda = ... keeps throwing the following error
Lambda type parameter must be derived from System.Delegate
Here is the code
public static IQueryable<dynamic> ToDynamic<T>(this IQueryable<T> query, IEnumerable<String> fields)
{
var pocoType = typeof(T);
var itemParam = Expression.Parameter(pocoType, "x");
var members = fields.Select(f => Expression.PropertyOrField(itemParam, f));
var addMethod = typeof(IDictionary<string, object>).GetMethod(
"Add", new Type[] { typeof(string), typeof(object) });
var elementInits = members.Select(m => Expression.ElementInit(addMethod, Expression.Constant(m.Member.Name), Expression.Convert(m, typeof(object))));
var expando = Expression.New(typeof(ExpandoObject));
var lambda = Expression.Lambda<Expression<Func<T, dynamic>>>(Expression.ListInit(expando, elementInits), itemParam);
return query.Select(lambda.Compile());
}
How can I correct this error?
In Expression.Lambda<T> expression T is considered lambda type parameter that must be derived from System.Delegate. So you just need to remove wrapping Expression and also there is no need to call Compile at the end since IQueryable expect to receive Expression<Func<T, F>> and not a Func<T, F> which is for IEnumerable
//..
var lambda = Expression.Lambda<Func<T, dynamic>>(Expression.ListInit(expando, elementInits), itemParam);
return query.Select(lambda);

Convert List.Contains to Expression Tree

Related To:
Create a Lambda Expression With 3 conditions
Convert Contains To Expression Tree
In the following of my previous question I faced with this query that I want to write Expression Tree version:
List<byte?> lst = new List<byte?>{1,2};
from a in myTbl
where a.Age = 20 && lst.Contains(a.Status)
select a
I write this code:
List<byte?> lst = new List<byte?>{1,2};
var param = Expression.Parameter(typeof(T), "o");
var body = Expression.AndAlso(
Expression.Equal(
Expression.PropertyOrField(param, "Age"),
Expression.Constant(20)
),
Expression.Call(Expression.PropertyOrField(param, "Status"),
"Contains",
Type.EmptyTypes,
Expression.Constant(lst)));
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
return lambda;
and I get the error:
"No method 'Contains' exists on type 'System.Nullable`1[System.Byte]'."
Please help me to find the problem.
Thanks
The difference from Convert Contains To Expression Tree is that there we were calling a string instance Contains method, while here we need to call a static generic method Enumerable.Contains:
public static bool Contains<TSource>(
this IEnumerable<TSource> source,
TSource value
)
It can be achieved by using another Expression.Call overload:
public static MethodCallExpression Call(
Type type,
string methodName,
Type[] typeArguments,
params Expression[] arguments
)
like this:
// Enumerable.Contains<byte?>(lst, a.Status)
var containsCall = Expression.Call(
typeof(Enumerable), // type
"Contains", // method
new Type[] { typeof(byte?) }, // generic type arguments (TSource)
Expression.Constant(lst), // arguments (source)
Expression.PropertyOrField(param, "Status") // arguments (value)
);
The problem is that you have switched two arguments to Expression.Call, your code is trying to create the nonsensical expression o.Status.Contains(lst).
You need to switch the two arguments around:
Expression.Call(Expression.Constant(lst),
"Contains",
Type.EmptyTypes,
Expression.PropertyOrField(param, "Status"))
This is assuming that the LINQ provider you're using understands List<T>.Contains(). If you need Enumerable.Contains(), then have a look at Ivan Stoev's answer.

Rewrite Lambda expression with System.Linq.Expressions APIs

I am stuck on this for hours. All I want to do is build an Expression tree by rewriting this following expression using Expression class APIs:
var Expression<Func<T, bool>> expr = x => x.SomeProperty == value;
What I got so far are:
{
var param = Expression.Parameter(typeof(T), "x");
var lhs = Expression.Property(param, "SomeProperty");
var rhs = Expression.Constant(value, value.GetType());
return Expression.Call(typeof(object).GetMethod("Equals", BindingFlags.Static | BindingFlags.Public), lhs, rhs);
}
This works fine if T is a primitive type or enumeration. But I got an exception if T is a reference type, a class etc.
Exception Message:
Unable to create a constant value of type 'TypeName'. Only primitive
types or enumeration types are supported in this context.
Thanks in advance.
You don't need to specify the type explicitly, in this case, as long as the value is not null (which I'm assuming it isn't, as you're calling GetType() on it).
This should do it.
var param = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(param, "SomeProperty");
var compareValue = Expression.Constant(value);
var equals = Expression.Equal(property, compareValue);
return Expression.Lambda<Func<T, bool>>(equals, param);
The expression generated was passed to a Linq Where call. Like this.
Context.Sources.Where(criteria.BuildQuery());
The exception was thrown when the expression is being evaluated/translated.
If I compile the expression and then pass a delegate to the Where call, everything works as expected.
Context.Sources.Where(criteria.BuildQuery().Compile());
I am not sure what difference it makes, if someone knows why please enlighten us.

Call Enumerable Average via expression

I'm trying to write dynamic code that do some aggregations Average, Sum, Max, etc.
That's the code im executing :
PropertyInfo sortProperty = typeof(T).GetProperty("PropertyName");
ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, sortProperty);
LambdaExpression orderByExp = Expression.Lambda(propertyAccess, parameter);
var exp = Expression.Lambda<Func<T, int>>(propertyAccess, parameter);
var call = Expression.Call(typeof(Enumerable), "Average", new[] { typeof(IEnumerable<T>) , typeof(Func<T, int>) }, parameter);
and I always get that exception:
No generic method 'Average' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
Let's look at this line. Here you're calling Call
var call = Expression.Call(typeof(Enumerable), "Average", new[] { typeof(IEnumerable<T>) , typeof(Func<T, int>) }, parameter);
The third parameter is "An array of Type objects that specify the type parameters of the generic method.". You're passing the types IEnumerable<T> and Func<T, int>, but Average takes only a single type parameter (TSource).
The forth parameter is "An array of Expression objects that represent the arguments to the method.". You're passing an expression representing a T, but Average expects an IEnumerable<TSource> and a Func<TSource, decimal> (or whatever overload you want to call, I'll just use the decimal one as an example).
I don't know what your final goal is using this code, but it probably should look like:
PropertyInfo sortProperty = typeof(T).GetProperty("Prop");
ParameterExpression parameter = Expression.Parameter(typeof(T), "p");
MemberExpression propertyAccess = Expression.MakeMemberAccess(parameter, sortProperty);
// parameter for the source collection
ParameterExpression source = Expression.Parameter(typeof(IEnumerable<T>), "source");
var exp = Expression.Lambda<Func<T, decimal>>(propertyAccess, parameter);
var call = Expression.Call(typeof(Enumerable), "Average", new[] {typeof(T)}, source, exp);
Here's a small example using this code (you'll get the idea):
// assuming a class called T with a decimal property called Prop
// because I'm a lazy and terrible person
var method = Expression.Lambda<Func<IEnumerable<T>, decimal>>(call, source).Compile();
var result = method(new List<T> {new T { Prop=10}, new T { Prop=20}});
// result is now 15

Complex LINQ sorting using Lambda Expressions

Does anyone have/know of an IQueryable.OrderBy extension that takes an Expression (retrieved, for example, by Reflection)? I believe the function would look something like this:
public static IQueryable<TEntity> OrderBy<TEntity>
(this IQueryable<TEntity> source, Expression sortExpression)
Expression would be assumed to be an Expression<Func<TEntity, T>> where TEntity is the same object being sorted on, and T is a type that needs to be determined in order to create the new IQueryable.
I've found many examples of extensions that take a string, including Dynamic Linq, like this:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source, string sortExpression)
If it's possible to take the string and use Reflection to look up the type from the object in question, it should also be possible to take the Expression, and get the value type which is right there IN the Expression.
Following is a detailed explanation of why I'd like to have this, which you may or may not need.
I have a rather large list of complex records to sort. Because the list is so long, I prefer to have the sorting done on the database side. To handle more complex properties, I've created Expressions that provide the sorting functionality, like so:
if (model.sortExpression == "PlannedValue")
{
Expression<Func<BDopp, decimal>> sorter = BDopp.PlannedValueSorter;
if (model.sortDirection == "DESC")
opps = opps.OrderByDescending(sorter).AsQueryable();
else
opps = opps.OrderBy(sorter).AsQueryable();
}
BDOpp.PlannedValueSorter retrieves a static expression from the object which allows sorting to be done without opps are still of type IQueryable:
public static Expression<Func<BDopp, decimal>> PlannedValueSorter
{
get
{
return z => z.BudgetSchedules
.Where(s => s.Type == 1)
.Sum(s => s.Value * s.Workshare * z.valueFactor / 100 / 100);
}
}
Sorting for simple properties is done with Extension methods that use Reflection to build an expression based on the name of the property passed as a string.
This works well, but for the complex types, I still need branching logic, and I'd rather not do that. What I'd rather do is check for a static property containing the expression, and then simply apply it. I can get the expression like this:
PropertyInfo info = typeof(BDopp).GetProperty(model.sortExpression + "Sorter",
BindingFlags.Static | BindingFlags.Public);
Expression expr = (Expression)info.GetValue(null, null);
For the PlannedValue property, this gets me the expression sorted in PlannedValueSorter, which I already know works.
Update:
Various digging around has gotten me what I think might be some progress:
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source,
Expression<Func<TEntity, dynamic>> sortExpression)
{
var unary = sortExpression.Body as UnaryExpression;
Type actualExpressionType = unary.Operand.Type;
actualExpressionType is in fact the return type of the Expression (for this particular property, it's decimal).
Unfortunately I'm mostly just working by trial and error, since I haven't yet wrapped my brain around how all this works, so my attempt to update the query like so is not working:
MethodCallExpression resultExp = Expression.Call(typeof(Queryable),
"OrderBy",
new Type[] { typeof(TEntity), actualExpressionType },
source.Expression, sortExpression);
return source.Provider.CreateQuery<TEntity>(resultExp);
It compiles okay, but the following error is thrown at runtime:
No generic method 'OrderBy' on type 'System.Linq.Queryable' is
compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic.
As I understand it you have an Expression and want to order/filter by it. It might surprise you how simple it is:
Expression<Func<TEntity, T>> sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy(sortExpr);
OrderBy always uses an Expression so you can directly use it. If you only have a variable of type Expression (which points to an object of type Expression<Func<TEntity, T>>) and don't know statically what T is, you can do this:
Expression sortExpr = ...; //you already have this
var ordered = myQuery.OrderBy((dynamic)sortExpr);
dynamic will figure this out at runtime using reflection. No need to do reflection yourself. All that is needed here is overload resolution (performed by the C# runtime binder).
Okay, I've got a solution:
public static IQueryable<TEntity> OrderBy<TEntity>(
this IQueryable<TEntity> source,
Expression<Func<TEntity, dynamic>> sortExpression,
bool descending)
{
var unary = sortExpression.Body as UnaryExpression;
var operand = unary.Operand;
Type actualExpressionType = operand.Type;
MethodCallExpression resultExp =
Expression.Call(typeof(Queryable),
descending? "OrderByDescending" : "OrderBy",
new Type[] { typeof(TEntity), actualExpressionType },
source.Expression,
Expression.Lambda(operand, sortExpression.Parameters));
return source.Provider.CreateQuery<TEntity>(resultExp);
}
The bool descending is to allow for the standard OrderBy and OrderByDescending overloads. Two major breakthroughs here, at least for me:
Getting the Operand out of the expression.
Using Expression.Call and Expression.Lambda to create the new expression - this allows me to use an actual "Type" variable, whereas the Expression<Func<TEntity, T>> syntax requires you to use a type that's known at compile time.
Try this:
public static IEnumerable<T> OrderBy<T>(
this IEnumerable<T> collection,
string columnName
//, SortDirection direction
)
{
ParameterExpression param = Expression.Parameter(typeof(T), "x"); // x
Expression property = Expression.Property(param, columnName); // x.ColumnName
Func<T, object> lambda = Expression.Lambda<Func<T, object>>( // x => x.ColumnName
Expression.Convert(property, typeof(object)),
param)
.Compile();
Func<IEnumerable<T>, Func<T, object>, IEnumerable<T>> expression = (c, f) => c.OrderBy(f); // here you can use OrderByDescending basing on SortDirection
IEnumerable<T> sorted = expression(collection, lambda);
return sorted;
}
Usage:
IEnumerable<T> collection = ...
IEnumerable<T> ordered = collection.OrderBy("PropName");
See Code Project and sandbox.

Categories