A. Savva
A. Savva

Reputation: 632

The type arguments for method Queryable.Select cannot be inferred from the usage

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

Answers (1)

xanatos
xanatos

Reputation: 111870

I'll quote @Ivan Stoev

The problem is that TResult needs to be known at compile time, so linqQuery (must) be IQueryable<string>, or IQueryable<int> etc. It's not possible to resolve the type of var at runtime. It's possible to emit the call to Select dynamically, but all you can get will be a non generic IQueryable, 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 untyped IQueryable.

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

Related Questions