LINQ Expression Conversion / Concat from Int to string - c#

I want to Concat two expressions for the final expression
Expression<Func<T, string>>
So I have created expression belwo code works fine for only string types , If I get memberExpression as Int32 or DateTime throwing exception
Expression of type 'System.Int32' cannot be used for parameter of type 'System.String' of method 'System.String Concat(System.String, System.String)'
If I convert the expression as
var conversion = Expression.Convert(memberExpression, typeof (string));
getting No coercion operator is defined between types 'System.Int32' and 'System.String'.
Please help me to resolve
Code
MethodInfo bodyContactMethod = typeof (string).GetMethod("Concat",new[] {typeof (string), typeof (string)});
ParameterExpression parameter = Expression.Parameter(typeof (T));
body = Expression.Call(bodyContactMethod, cons, memberExpression);
return Expression.Lambda<Func<T, string>>(body, parameter);

Instead of trying to cast to string, you could try casting to object then calling ToString(), as though you were doing:
var converted = member.ToString();
As an Expression, it will look something like this:
var convertedExpression = Expression.Call(
Expression.Convert(memberExpression, typeof(object)),
typeof(object).GetMethod("ToString"));

It can be further simplified to:
var convertedExpression = Expression.Call(
memberExpression,
typeof(object).GetMethod("ToString"));

Rather than calling string.Concat(string, string), you could try calling string.Concat(object, object):
MethodInfo bodyContactMethod = typeof (string).GetMethod("Concat",
new[] { typeof(object), typeof(object) });

To expand on Richard Deeming's answer even though it's a little late.
Expression.Call(
typeof(string).GetMethod("Concat", new[] { typeof(object), typeof(object) }),
Expression.Convert(cons, typeof(object)),
Expression.Convert(memberExpression, typeof(object))
);
That should work just fine while allowing the signature to stay as you have it.

Related

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.

Linq Expressions -> Create CallExpression with Equals using enum types

I'm trying to build an CallExpression like:
f.Equals(s);
where, f and t are enum SType.
So,
var equalsMethod = typeof(SType).GetMethod("Equals", new[] { typeof(SType) });
ConstantExpression constantExpression = Expression.Constant(value, typeof(SType));
var newBody = Expression.Call(expr.Body, equalsMethod, constantExpression);
return Expression.Lambda<Func<TEntity, bool>>(newBody, expr.Parameters);
I don't know, but equalsMethod is Boolean Equals(System.Object) instead of Boolean Equals(SType).
So, when I want to build CallExpression .Net tells me, I'm not able to use an expression of type SType for the parameter of type System.Object of the method Boolean Equals(System.Object).
What's wrong?
When you call f.Equals(s) you're really doing:
f.Equals((object)s)
... because Enum and ValueType don't overload Equals. So basically you need a conversion in there - and you can be clearer about the Equals method you're calling, too:
var equalsMethod = typeof(object).GetMethod("Equals", new[] { typeof(object) });
var constant = Expression.Constant(value, typeof(SType));
var boxed = Expression.Convert(constant, typeof(object));
var newBody = Expression.Call(expr.Body, equalsMethod, boxed);
return Expression.Lambda<Func<TEntity, bool>>(newBody, expr.Parameters);
Admittedly you could probably avoid the separate step, just with:
var equalsMethod = typeof(object).GetMethod("Equals", new[] { typeof(object) });
// Let this do the boxing for you...
var constant = Expression.Constant(value, typeof(object));
var newBody = Expression.Call(expr.Body, equalsMethod, constant);
return Expression.Lambda<Func<TEntity, bool>>(newBody, expr.Parameters);
I haven't tried that, but I suspect it will work just fine.
Jon already described what's wrong with your code. However, I'm wondering why you ever bother creating MethodCallExpression to Equals method while there is a specific Expression.Equal method just for that (well, to be precise, for doing equality comparison):
return Expression.Lambda<Func<TEntity, bool>>(
Expression.Equal(expr.Body, Expression.Constant(value)),
expr.Parameters);

Get OrderBy method using reflection

I want to implement generic pager and filter View Model for my project and I'm stuck on getting OrderBy method using reflection. Here is what I've tried, but keep getting null for methodInfo. It seems I'm passing the wrong Type[] arguments to the GetMethod() Method, but I can't get it right.
protected virtual Expression<Func<T, IComparable>> GetOrderByExpression()
{
var type = typeof(T);
var property = type.GetProperty("DataSetName");
var parameter = Expression.Parameter(type, "x");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var methodInfo = typeof(Enumerable).GetMethod("OrderBy", new Type[] { orderByExp.Body.Type });
var predicateBody = Expression.Call(propertyAccess, methodInfo, orderByExp);
var expression = Expression.Lambda<Func<T, IComparable>>(predicateBody, parameter);
return expression;
}
Then Enumerable.OrderBy extension method is a static method so you have to use an overload to GetMethod where you can specify BindingFlags (BindingFlags.Static | BindingFlags.Public instead of the default BindingFlags.Instance | BindingFlags.Public).
You also have to specify two parameters to the method - syntactically it looks like there is only one parameter but because it is an extension method there is a second parameter which you have to specify.
This is the method you want to get via reflection:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector
)
You need the two parameter types:
var sourceType = typeof(IEnumerable<>).MakeGenericType(type);
var keySelectorType = orderByExp.Compile().GetType();
It seems that the only way to get a specific overload of a method with generic parameters you have to perform a search. Fortunately, there are only two overloads of Enumerable.OrderBy and the one you want is the one with two parameters:
var genericMethodInfo = typeof(Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.First(mi => mi.Name == "OrderBy" && mi.GetParameters().Length == 2);
var methodInfo = genericMethodInfo.MakeGenericMethod(sourceType, keySelectorType);
This returns the desired MethodInfo, however you will have to modify the remaining two lines of code because this method is a static method and you have to specify a null instance in Expression.Call.

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

Categories