Expression of type 'System.Int64' cannot b

2019-04-11 10:32发布

问题:

I am trying to create an expression of the following form:

e => e.CreationDate;

CreationDate is of type long, however I want the expression to return an object instead.

I want to use object as a return type because the expression is built dynamically at runtime based on a query paramater. The query parameter specifies the property to access in the expression, such as:

> entities?order=creationDate
> entities?order=score

As you can see, I can order by different properties with different types, so a return type object would allow me to build the expression as generic as possible.

The problem is that when I try to create the expression:

ParameterExpression entityParameter = Expression.Parameter(typeof(Entity), "e");
Expression propertyAccess = Expression.Property(entityParameter, property);
Expression<Func<Entity, object>> result = Expression.Lambda<Func<Entity, object>>(propertyAccess, entityParameter);

I get the following exception:

Expression of type 'System.Int64' cannot be used for return type 'System.Object'

It is strange, because as far as I know, all types extend from object (It seems polymorphism is not yet supported by expression trees).

Nevertheless, I searched on the web and stumbled with this similar question:

Expression of type 'System.Int32' cannot be used for return type 'System.Object'

Following Jon Skeet's answer, I modified my last line to:

Expression<Func<Entity, object>> result = Expression.Lambda<Func<Entity, object>>(Expression.Convert(propertyAccess, typeof(object)), entityParameter);

This works fine, but it doesn't generate the expression I want. Instead, it generates something like this:

e => Convert(e.CreationDate)

I cannot use this solution, because later in the program an exception is thrown if the expression body is not a MemberExpression (i.e., a member access operation)

I kept searching on the Internet for a satisfying answer, but couldn't find any.

How can I achieve e => e.CreationDate where return type is object?

回答1:

Depending on how you use result you could create it dynamically with the delegate type Func<Entity, long> and type it as a LambdaExpression:

ParameterExpression entityParameter = Expression.Parameter(typeof(Entity), "e");
Expression propertyAccess = Expression.Property(entityParameter, property);
var funcType = typeof(Func<,>).MakeGenericType(typeof(Entity), property.PropertyType);
LambdaExpression result = Expression.Lambda(funcType, propertyAccess, entityParameter);


回答2:

Short answer: no, it is not possible. Value types need to be boxed to be seen as objects. The compiler does it for you normally, but if you build code yourself (e.g expression trees), you need to specify it as an explicit conversion, the way you see it in the found answer. If you cannot have it as a non-generic LambdaExpression, I would handle the convert case additionally where you expect the MemberExpression, or use PropertyInfo, and construct the orderby Expression only in the last moment.