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
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);