Dynamic call of a Lambda expression

I get this exception when I run this code.

ParametrVyrazhenie type System.Int64 can not be used for delegating the parameter type System.Object

I know this is due to the code part of Expression.Lambda<func<object,bool>> . In general, I want to pass any type of ParameterExpression to this method, and it will call an expression.

 public static IQueryable<T> OrderData<T>(IQueryable<T> data) { try { Order order = Order.ASC; var result = Enum.TryParse<Order>(_gridSettings.SortOrder, true, out order); if (_gridSettings.IsSearch) { data = ExpressionSort(order, data, typeof(T).GetProperty(_gridSettings.SortColumn)); } else { data = ExpressionSort(order, data, _defaultColumn); } } catch (Exception ex) { log.WriteLog(MethodBase.GetCurrentMethod(), LogLevel.FATAL, ex); } return data; } private static IQueryable<T> ExpressionSort<T>(Order order, IQueryable<T> data, PropertyInfo property) { // Compose the expression tree that represents the parameter to the predicate. ParameterExpression paramExpression = Expression.Parameter(property.PropertyType, property.Name); IQueryable<T> queryableData = data.AsQueryable<T>(); switch (order) { case Order.ASC: return ExecuteCall(paramExpression, paramExpression, queryableData, "OrderBy"); case Order.DESC: return ExecuteCall(paramExpression, paramExpression, queryableData, "OrderByDescending"); } return data; } private static IQueryable<T> ExecuteCall<T>(Expression expression, ParameterExpression paramExpression, IQueryable<T> queryableData, string linqMethod) { MethodCallExpression callExpression = Expression.Call( typeof(Queryable), linqMethod, new Type[] { queryableData.ElementType }, queryableData.Expression, Expression.Lambda<Func<object, bool>>(expression, new ParameterExpression[] { paramExpression })); // Create an executable query from the expression tree. return queryableData.Provider.CreateQuery<T>(callExpression); } 

EDIT: I saw this answer to a similar question

An expression like 'System.Int32' cannot be used for the return type 'System.Object' I don't know how to apply it to my code, though

EDIT 2: The main problem is that this line is Expression.Lambda<Func<object, bool>>(conversion, new ParameterExpression[] { paramExpression })); gives me an exception. paramExpression contains Int64, but an object expects it. I do not know how to dynamically report Func from the information that I already have, or if it is possible.

TASK: I am trying to do something like this data.OrderBy(x=>x.DynamicProperty);

+6
source share
3 answers

This is what you asked for, I think ... I tested it and it seems to work.

 // Caching of the reflection private static readonly MethodInfo orderByMethod = GetOrderByMethod("OrderBy"); private static readonly MethodInfo orderByDescendingMethod = GetOrderByMethod("OrderByDescending"); private static IOrderedQueryable<TSource> ExpressionSort<TSource>(Order order, IQueryable<TSource> source, PropertyInfo property) { // Compose the expression tree that represents the parameter to // the predicate. // The expression you would use is source => source.Property, // The parameter of the lambda, source ParameterExpression sourceExpression = Expression.Parameter(typeof(TSource), "source"); // Accessing the expression MemberExpression propertyExpression = Expression.Property(sourceExpression, property); // The full lambda expression. We don't need the // Expression.Lambda<>, but still the keySelector will be an // Expression<Func<,>>, because Expression.Lambda does it // authomatically. LambdaExpression is simply a superclass of // all the Expression<Delegate> LambdaExpression keySelector = Expression.Lambda(propertyExpression, sourceExpression); // The OrderBy method we will be using, that we have cached // in some static fields MethodInfo method = order == Order.ASC ? orderByMethod : orderByDescendingMethod; // Adapted from Queryable.OrderBy (retrieved from the reference // source code), simply changed the way the OrderBy method is // retrieved to "method" return (IOrderedQueryable<TSource>)source.Provider.CreateQuery<TSource>(Expression.Call(null, method.MakeGenericMethod(new Type[] { typeof(TSource), property.PropertyType }), new Expression[] { source.Expression, Expression.Quote(keySelector) })); } private static MethodInfo GetOrderByMethod(string methodName) { // Here I'm taking the long and more correct way to find OrderBy/ // OrderByDescending: looking for a public static method with the // right name, with two generic arguments and that has the // parameters related to those two generic arguments in a certain // way (they must be IQueryable<arg0> and Expression<Func<arg0, // arg1>> MethodInfo orderByMethod = (from x in typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public) where x.Name == methodName let generics = x.GetGenericArguments() where generics.Length == 2 let parameters = x.GetParameters() where parameters.Length == 2 && parameters[0].ParameterType == typeof(IQueryable<>).MakeGenericType(generics[0]) && parameters[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(generics)) select x).Single(); return orderByMethod; } 

Please never use AsQueryable<>() . It does not do what you think, and it is absolutely useless outside of unit testing and very specific use cases.

+3
source

Try using Expression.Convert . Here is a similar question that may give you some more recommendations:

An expression of type 'System.Int32' cannot be used for the return type 'System.Object'

0
source

You can use the OrderByString extension. https://www.nuget.org/packages/OrderByString/ It takes to string collation. Lines of sorting options can be separated by comma-based lists of property names, such as "Prop1, Prop2", or may contain sorting order, as in "Prop1 DESC, Prop2 ASC".

 using OrderByExtensions; public static IQueryable<T> OrderData<T>(IQueryable<T> data) { try { Order order = Order.ASC; var result = Enum.TryParse<Order>(_gridSettings.SortOrder, true, out order); var sortColumn = _gridSettings.IsSearch ? _gridSettings.SortColumn : _defaultColumn; data = data.OrderBy(sortColumn + " " + _gridSettings.SortOrder.ToString()); } catch (Exception ex) { log.WriteLog(MethodBase.GetCurrentMethod(), LogLevel.FATAL, ex); } return data; } 

OR

You can use the following GetExpressionForProperty method, which returns the expected sort expression for OrderBy, OrderByDescending, ThenBy, or ThenByDescending.

 private static IQueryable<T> ExpressionSort<T>(Order order, IQueryable<T> data, PropertyInfo property) { Expression<Func<T, object>> propertyExpression = GetExpressionForProperty<T>(property); return order == Order.DESC ? data.OrderByDescending(propertyExpression) : data.OrderBy(propertyExpression); } static Expression<Func<TSource, object>> GetExpressionForProperty<TSource>(PropertyInfo propertyInfo) { var param = Expression.Parameter(typeof(TSource)); return Expression.Lambda<Func<TSource, object>>( Expression.Convert( Expression.Property(param, propertyInfo), typeof(object) ) , param); } 
0
source

Source: https://habr.com/ru/post/987319/


All Articles