0
votes

I am trying to create an expression tree for this expression Table.OrderBy(x => Math.Abs(x.Temperature - temp)) to get the closest temperature value

I have an extension method that already sorts by property, however I am unsure how to insert the Math.Abs expression.

  public IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder)
    {
        var type = typeof(T);
        var property = type.GetProperty(sortProperty);
        var parameter = Expression.Parameter(type, "p");
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var orderByExp = Expression.Lambda(propertyAccess, parameter);
        var typeArguments = new Type[] { type, property.PropertyType };
        var methodName = sortOrder == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
        var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));

        return source.Provider.CreateQuery<T>(resultExp);
    }

EDIT:

Here is the solution I found. Thank you for your help!

    /// <summary>
    /// Gets the TEntity value closest to the given property value
    /// </summary>
    /// <param name="property">Property name</param>
    /// <param name="value">Property value</param>
    /// <returns></returns>
    public TEntity GetNearestEntity(string property, double value)
    {
        //Error handling, if property doesn't have a double value type
        if (typeof(TEntity).GetProperty(property).PropertyType != typeof(double)) return null;

        //Parameter in lambda
        ParameterExpression param = Expression.Parameter(typeof(TEntity), "x");

        //Create member property [ property == ...]
        MemberExpression member = Expression.Property(param, typeof(TEntity).GetProperty(property));

        //Create constant expression [ ... == value]
        ConstantExpression constExp = Expression.Constant(value);

        //Binary expression [param.property - value]
        BinaryExpression binaryExp = Expression.Subtract(member, constExp);

        //Gets method info for Math.Abs
        MethodInfo method = typeof(Math).GetMethod("Abs", new[] { binaryExp.Type });

        //Creates method Math.Abs(x.Property - value)
        var absCallExpression = Expression.Call(null, method, binaryExp);

        //Create lambda
        Expression<Func<TEntity, double>> lambda = Expression.Lambda<Func<TEntity, double>>(absCallExpression, new ParameterExpression[] { param });

        //Gets single entity from DbSet
        TEntity entity = _entity.OrderBy(lambda).FirstOrDefault();

        return entity;
    }
1

1 Answers

0
votes

You should do it on the propertyAccess (Btw your code sample doesn't look complete because I don't see the subtracting expression):

var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var absMethodInfo = typeof(Math).GetMethod("Abs", new[]{propertyAccess.Type});
var absCallExpression = Expression.Call(null, absMethodInfo, propertyAccess);
var orderByExp = Expression.Lambda(absCallExpression, parameter);
// ...

Idea is to search the Math.Abs method (make sure this search is successful by asserting that absMethodInfo is not null) and generate a method call expression on the property access (Expression.MakeMemberAccess(parameter, property)) - constant expression (this part is missing in your code sample). Hope this helps.