Reputation: 632
I am using a LambdaExpression
to select columns dynamically:
var property = "power_usage"; // I set this dynamically.
var entityType = typeof(system_state);
var prop = entityType.GetProperty(property);
var source = Expression.Parameter(entityType, "ss");
var func = typeof(Func<,>);
var genericFunc = func.MakeGenericType(typeof(system_state), prop.PropertyType);
var linqQuery = context.system_state
.Where(ss => ss.time_stamp >= StartDate && ss.time_stamp <= EndDate)
.Select(genericFunc, Expression.PropertyOrField(source, property), source);
The variable genericFunc
is supposed to define the delegateType
, but I still get this error. What am I doing wrong?
Upvotes: 0
Views: 1902
Reputation: 111870
I'll quote @Ivan Stoev
The problem is that
TResult
needs to be known at compile time, solinqQuery
(must) beIQueryable<string>
, orIQueryable<int>
etc. It's not possible to resolve the type ofvar
at runtime. It's possible to emit the call toSelect
dynamically, but all you can get will be a non genericIQueryable
, which is not so useful. You might take a look at DynamicLINQ package to see how it is addressing this and similar problems. But even with it it's a pain to work with untypedIQueryable
.
Then if you really want to do it... In the end it is your code :-)
This can be cached:
private static readonly MethodInfo selectT = (from x in typeof(Queryable).GetMethods(BindingFlags.Public | BindingFlags.Static)
where x.Name == nameof(Queryable.Select) && x.IsGenericMethod
let gens = x.GetGenericArguments()
where gens.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IQueryable<>).MakeGenericType(gens[0]) &&
pars[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(gens))
select x).Single();
Then:
var property = "power_usage"; // I set this dynamically.
var entityType = typeof(system_state);
var prop = entityType.GetProperty(property);
var source = Expression.Parameter(entityType, "ss");
var func = typeof(Func<,>);
var genericFunc = func.MakeGenericType(typeof(system_state), prop.PropertyType);
var baseQuery = context.system_state
.Where(ss => ss.time_stamp >= StartDate && ss.time_stamp <= EndDate);
var exp = Expression.Lambda(Expression.Property(source, prop), source);
MethodInfo select = selectT.MakeGenericMethod(entityType, prop.PropertyType);
IQueryable query = (IQueryable)select.Invoke(null, new object[] { baseQuery, exp });
var result = query.Cast<object>().ToArray();
Note that I'm obtaining a non-generic IQueryable
... Then I cast its elements to object
and do a ToArray()
, but you can do whatever you want with it. Underneath the IQueryable
will be strongly typed, so it will be a IQueryable<int>
or a IQueryable<something>
, so you can cast it back to the "real" interface (IQueryable<T>
inherits from IQueryable
)
Upvotes: 2