Using Expression.Call with Queryable.Select with a type known only at runtime

本秂侑毒 提交于 2020-01-23 17:49:07

问题


I am trying to select a column from an IEnumerable collection that has a type known only to me at runtime. The only way I can think of using this is using LINQ expressions to build a dynamic call to Queryable.Select. However, I'm having a lot of trouble figuring out the proper syntax to accomplish this.

An example of how I would do this in the happy-go-lucky world of knowing everything I need at compile time, my code would look like this:

' Create an IEnumerable(Of String)
Dim strings = { "one", "two", "three" }

' Produce a collection with elements {3,3,5}
Dim stringLengths = strings.Select(Function(x) x.Length)

Unfortunately, in reality I have no idea that the collection I have is of type String, or that the property I want to select is Length. What I do have is an IEnumerable collection of items, as well as a PropertyInfo of the column that I want to select which provides me all of the type information I need.

As far as expressions go, I've been able to create a LINQ expression that I believe would represent the lambda I would normally pass to select ( Assume I am trying to perform the same operation above, with String and String.Length)

' pi is the PropertyInfo containing the Length property I am trying to select.
' pi.DeclaringType is String and pi.Name is Length
Dim targetItem = Expression.Parameter(pi.DeclaringType, "x")
Dim targetProperty = Expression.Property(targetItem, pi.Name)

' Produces the lambda<Function(x) x.Length>
Dim selectLambda = Expression.Lambda(targetProperty, targetItem)

Now hopefully all that's left is to build is the call to Queryable.Select. To me, the syntax of Expression.Call is pretty confusing to say the least. My attempt is as follows (which fails with no errors or explanation of any kind):

' Creates a Parameter Expression of type IQueryable(Of String)
Dim source = Expression.Parameter(GetType(IQueryable(Of )).MakeGenericType(pi.DeclaringType), "source")

' Ideally, this would create an expression for a call to Queryable.Select
Dim selectCall = Expression.Call(GetType(Queryable), "Select", {pi.DeclaringType, pi.PropertyType}, source)

I tried doing this in an alternative way without using the Type[] parameter and using the expressions for my item and property to no avail:

Dim alternateExp = Expression.Call(GetType(Queryable), "Select", Nothing, {targetProperty, item})

The problem is that I'm pretty much just guessing at this point. However, the whole idea of building the function call, when to use Types or Expressions, which types or expressions to use, or where to even use them is just plain confusing. Any help getting me the last part of the way there and clearing some of this mystery would be greatly appreciated. (I'm perfectly happy with examples in C#)


回答1:


 var propertyType = typeof (string);
 var propertyName = "Length";
 IEnumerable list = new ArrayList { "one", "two", "three" };


  var item = Expression.Parameter(typeof(object), "x");
  var cast = Expression.Convert(item, propertyType);
  var propertyValue = Expression.PropertyOrField(cast, propertyName);
  var propertyValueAsObject = Expression.Convert(propertyValue, typeof (object));
  var selectLambda = Expression.Lambda<Func<object, object>>(propertyValueAsObject, item);

  list.Cast<object>().AsQueryable().Select(selectLambda);

This is an answer using expressions, basically we deal with everything as an Object (casting to our runtime type and then casting back to Object for the final result.



来源:https://stackoverflow.com/questions/11312570/using-expression-call-with-queryable-select-with-a-type-known-only-at-runtime

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!