Expression Tree for Enumerable.Select

c# expression-trees

Question

Edit I think I can ask better (no code needed at all in this case). So the question in general is: how to use an Expression tree to build a call to a generic method (Select<TEntity,TResult> in my case) when TResult is created at runtime? Ignore all the code and text below, that was unclear version of the question, left it to not confuse those who answered.

I need an example how to build an Expression tree for a "Select" call. The problem is that I don't know the result type at compile time (it can be defined by user through some GUI in runtime). Here is some code of how I'm trying to do this (remember I can't use any generics)

class Pet {
    public int Id { get; set; }
    public string Name { get; set; }
}

Using this class in Main:

List<Pet> list = new List<Pet>();                
Expression eList = Expression.Constant(list);
ParameterExpression pe = Expression.Parameter(typeof(Pet), "p");
MethodInfo method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
Expression selectorBody = Expression.Call(method, Expression.Constant(properties));
Expression selector = Expression.Lambda(selectorBody, pe);
Expression res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector);

What I'm trying to do is create a Type at runtime using Reflection.Emit (method "CreateType" in code above, it is used in some of my projects and looks fine) and somehow pass it to call to "Select", but I get exception:

No generic method 'Select' on type 'System.Linq.Enumerable' is compatible with the supplied type arguments and arguments.

Any suggestions?

Upd my real problem is building an Expression tree for a "Join" call for runtime types which is more complicated, so i decided to ask about "Select", because I'm having the same exception in the last line (Expression.Call(...))

Upd2 included my helper methods and edited main code, however this is not the main problem.

static Type CreateType(IEnumerable<PropertyInfo> properties) {
        TypeBuilder typeBuilder = CreateTypeBuilder("ResultDynamicAssembly", "ResultModule", "ResultType");
        foreach (PropertyInfo propertyInfo in properties) {
            CreateAutoImplementedProperty(typeBuilder, propertyInfo.Name, propertyInfo.PropertyType);
        }
        return typeBuilder.CreateType();
    }

    static object CreateObject(IEnumerable<PropertyInfo> properties) {
        Type type = CreateType(properties); 
        return Activator.CreateInstance(type);
    }

Accepted Answer

You can simplify this problem by removing the dynamic type from the equation. You can reproduce the same issue with the code below, which does exactly the same thing, but without the dynamic type.

static class Program
{
    private static void Main(string[] args)
    {
        var list = new List<Pet>();
        var eList = Expression.Constant(list);
        var pe = Expression.Parameter(typeof(Pet), "p");
        var method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
        var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
        var selectorBody = Expression.Call(method, Expression.Constant(properties));
        var selector = Expression.Lambda(selectorBody, pe);
        var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector);
    }

    private static Type CreateType(IEnumerable<PropertyInfo> properties)
    {
        return typeof (DynamicType);
    }

    private static object CreateObject(IEnumerable<PropertyInfo> properties)
    {
        var type = CreateType(properties);
        return  Activator.CreateInstance(type);
    }

    class Pet
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    class DynamicType
    {
        public string Name { get; set; }
    }
}

So the problem is the method signature of CreateObject. Since its return type isn't the dynamic type, the lambda isn't valid. You can see this by changing the type of CreateObject.

    // this works fine
    private static DynamicType CreateObject(IEnumerable<PropertyInfo> properties)
    {
        var type = CreateType(properties);
        return  (DynamicType) Activator.CreateInstance(type);
    }

Since you're dealing with a dynamic type, you need to build an expression to cast the result of CreateObject to your dynamic type. Try using something like this:

        // create dynamic type
        var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
        var dynamicType = CreateType(properties);

        // build expression
        var list = new List<Pet>();
        var eList = Expression.Constant(list);
        var pe = Expression.Parameter(typeof(Pet), "p");
        var createObjectMethod = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
        var createObjectCall = Expression.Call(createObjectMethod, Expression.Constant(properties));
        var castExpression = Expression.Convert(createObjectCall, dynamicType);
        var selectorExpression = Expression.Lambda(castExpression, pe);
        var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), dynamicType }, eList, selectorExpression);


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why