linqexpression-treestypebuilder

'Argument expression is not valid' when creating anonymous types dynamically


I'm creating an expression tree builder to return custom anonymous types. I tried it first with discrete types and it works ok, but using TypeBuilder to build types at runtime and pass that type to the expression tree fail with this error

'Argument expression is not valid'

here is the code I use:

this method I use to create the anonymous type

private Type CreateAnonymousType(Dictionary<string, Type> properties)
    {
      AssemblyName dynamicAssemblyName = new AssemblyName("MyAssembly");
      AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
      ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("MyAssembly");

      TypeBuilder dynamicAnonymousType = dynamicModule.DefineType("ReturnType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass);
      foreach (var p in properties)
      {
        dynamicAnonymousType.DefineField(p.Key, p.Value, FieldAttributes.Public);
      }

      return dynamicAnonymousType.CreateType();
    }

and here is how I create the expression tree

var cars = new List<Car>();
      for (int i = 0; i < 10; i++)
      {
        cars.Add(new Car { Id = i, Name = "Car " + i, Age = 2010 + i });
      }


      IQueryable<Car> allCars = cars.AsQueryable();

      var properties = new Dictionary<string, Type>
      {
        { "Id", typeof(int) },
        { "Name", typeof(string) }
      };

      ParameterExpression x = Expression.Parameter(typeof(Car), "x");
      var listMembers = properties.Select(p => Expression.Property(x, p.Key));

      var returnType = CreateAnonymousType(properties);
      object destObject = Activator.CreateInstance(returnType);


      var listBind = listMembers.Select(p => Expression.Bind(returnType.GetField(p.Member.Name), p));

      var result = Expression.New(returnType);
      var initExp = Expression.MemberInit(result, listBind.ToArray());


      var call = Expression.Call(typeof(Queryable), "Select",
          new Type[] {
            typeof(Car),
            returnType
        }
        , Expression.Constant(allCars)
        , Expression.Lambda(initExp, x));

      var qResult = allCars.Provider.CreateQuery<IdName>(call);

      foreach (var car in qResult)
      {
        Console.WriteLine(car.Id + " - " + car.Name);
      }

the error happened while CreateQuery method executes


Solution

  • This is because call returns dynamically created ReturnType not IdName thus the exception. Additionally you cannot put such dynamic types like ReturnType as generic type parameters because compiler knows nothing about them so you should use dynamic instead so the type will be resolved at runtime:

    var qResult = allCars.Provider.CreateQuery<dynamic>(call);