I would like to dynamically build the linq expression used in the following Select statement on a DataTable filled from a DB:
IQueryable iq = myDataTable
.AsEnumerable()
.AsQueryable()
.Select(
d => new {
Field1 = d.Field<int>(37),
Field2 = d.Field<string>(0),
Field3 = d.Field<DateTime>(1)
}
);
The fields are not known before runtime. I will get the list of fields ("Field1", "Field2", "Field3"...), their types (int, string, datetime...) and their indexes (37, 0, 1...) at runtime.
Is there any way to achieve that?
[UPDATE]
I started to write the following code:
Type type = typeof(DataRow);
MethodInfo mi1 = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(int) });
mi1 = mi1.MakeGenericMethod(type);
List<Expression> list1 = new List<Expression>();
ParameterExpression datarow1 = Expression.Parameter(type, "datarow");
ConstantExpression constant1 = Expression.Constant(37, typeof(int));
list1.Add(datarow1);
list1.Add(constant1);
ReadOnlyCollection<Expression> collection1 = new ReadOnlyCollection<Expression>(list1);
MethodCallExpression right1 = Expression.Call(null, mi1, datarow1, constant1);
Expression left1 = Expression.Parameter(type, "Field1");
var expression1 = Expression.Assign(left1, right1);
MethodInfo mi2 = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(int) });
mi2 = mi2.MakeGenericMethod(type);
List<Expression> list2 = new List<Expression>();
ParameterExpression datarow2 = Expression.Parameter(type, "datarow");
ConstantExpression constant2 = Expression.Constant(0, typeof(int));
list2.Add(datarow2);
list2.Add(constant2);
ReadOnlyCollection<Expression> collection2 = new ReadOnlyCollection<Expression>(list2);
MethodCallExpression right2 = Expression.Call(null, mi2, datarow2, constant2);
Expression left2 = Expression.Parameter(type, "Field2");
var expression2 = Expression.Assign(left2, right2);
List<Expression> bindings = new List<Expression>();
bindings.Add(expression1);
bindings.Add(expression2);
var newInit = Expression.New(typeof(object));
var init = Expression.NewArrayInit(type, bindings);
var dr = Expression.Parameter(type, "dr");
var linq = Expression.Lambda<Func<DataRow, DataRow>>(init, dr);
It compiles, however, there are several problems:
- expression1 is
(Field1 = datarow.Field(0))
instead ofField1 = datarow.Field<int>(0)
- same for expression 2
- it throws a runtime exception:
'System.Data.DataRow[]' cannot be used for return type 'System.Data.DataRow'
on the linevar linq = Expression.Lambda...
Could you please help?
@George I think I do not need a Type dictionary as I have a set of predefined types.