1

I am building a helper method that returns an expression based on a property that could be used in an orderby or where, etc. in a linq to entities operation.

I won't know the type of the property upfront, so I have declared it as object and dynamic and have also tried to use Expression.Convert but it will not work for non string typed properties.

The current property type that is not a string that I'm working with is int? and the error I get is

Expression of type 'System.Nullable`1[System.Int32]' cannot be used for return type 'System.Object';

Code:

var param = Expression.Parameter(typeof(Employee), "x");
MemberExpression propExp = Expression.Property(param, "somePropertyName");
Expression.Lambda<Func<Employee, object>>(propExpression, param);

AS I said, I've used object and dynamic in line above with same results. I've also tried to convert it to the right type but that doesn't work:

Expression conversion = Expression.Convert(propExp, ((PropertyInfo)propExp.Member).PropertyType)

While I'm in debug mode and I try this, Expression.Lambda(conversiona, param), it seems to work

{x => Convert(x.EmployeeNo)}
Body: {Convert(x.EmployeeNo)}
CanReduce: false
DebugView: ".Lambda #Lambda1<System.Func`2[xx.DomainModel.Entities.Employee,System.Nullable`1[System.Int32]]>(xx.DomainModel.Entities.Employee $x)\r\n{\r\n    (System.Nullable`1[System.Int32])$x.EmployeeNo\r\n}"
Name: null
NodeType: Lambda
Parameters: Count = 1
ReturnType: {Name = "Nullable`1" FullName = "System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}
TailCall: false
Type: {Name = "Func`2" FullName = "System.Func`2[[xx.DomainModel.Entities.Employee, Fng.Facts.DomainModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}

...but when I use it in an orderby I get

error CS1503: Argument 2: cannot convert from 'System.Linq.Expressions.LambdaExpression' to 'string'

I thought that maybe I needed to dynamically build the Expression.Lambda using the correct type as I inspect it but haven't tried that yet

Thanks for any help

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
ManOfSteele
  • 281
  • 1
  • 15
  • 2
    Rather than trying to build an expression, try to build a whole `OrderBy(Descending)` / `ThenBy(Descending)` call. Otherwise you'll have the same issue as in [How can I specify a predicate's type, which I won't know until runtime?](http://stackoverflow.com/questions/40544393/how-can-i-specify-a-predicates-type-which-i-wont-know-until-runtime/40545008#comment68329807_40545008) – Ivan Stoev Nov 12 '16 at 18:24
  • Possible duplicate of [How to use a string to create a EF order by expression?](http://stackoverflow.com/questions/39908403/how-to-use-a-string-to-create-a-ef-order-by-expression) – Ivan Stoev Nov 13 '16 at 10:35

1 Answers1

1

Convert it to object by

Expression conversion = Expression.Convert(propExp, typeof(object))

That should be ok to be returned, but then you will have to deal with these converts on the other side, where you do the Orderby.

    private IQueryable<T> AddOrderBy<T>(IQueryable<T> query, Expression<Func<T, object>> orderByProperty, bool isAscending, bool isFirst)
{
    Expression<Func<IOrderedQueryable<int>, IQueryable<int>>> methodDef = isAscending 
        ? (isFirst ? (Expression<Func<IOrderedQueryable<int>, IQueryable<int>>>)(q => q.OrderBy(x => x)) : (Expression<Func<IOrderedQueryable<int>, IQueryable<int>>>)(q => q.ThenBy(x => x)))
        : (isFirst ? (Expression<Func<IOrderedQueryable<int>, IQueryable<int>>>)(q => q.OrderByDescending(x => x)) : (Expression<Func<IOrderedQueryable<int>, IQueryable<int>>>)(q => q.ThenByDescending(x => x)));

    // get the property type
    var propExpression = orderByProperty.Body.NodeType == ExpressionType.Convert && orderByProperty.Body.Type == typeof(object)
        ? (LambdaExpression)Expression.Lambda(((UnaryExpression)orderByProperty.Body).Operand, orderByProperty.Parameters)
        : orderByProperty;

    var methodInfo = ((MethodCallExpression)methodDef.Body).Method.GetGenericMethodDefinition().MakeGenericMethod(typeof(T), propExpression.Body.Type);
    return (IQueryable<T>)methodInfo.Invoke(null, new object[]{query, propExpression});
}
MBoros
  • 1,090
  • 7
  • 19
  • Thanks....the idea was to pass in a string property name to the method. Also, could it be turned into an extension method? Is it also possible to support IEnumerable too? – ManOfSteele Nov 14 '16 at 21:44
  • If you have a string as input, then you dont even have to worry about the convert, just say: var param = Expression.Parameter(typeof(T)); var propExpression = Expression.Lambda(Expression.MakeMemberAccess(param, propertyName), param); and then use that in the invoke – MBoros Nov 17 '16 at 08:01
  • For IEnumerables, I guess its the same code, question is if your db supports it ??? – MBoros Nov 17 '16 at 08:03