2017-05-03 6 views
0

J'utilise un LambdaExpression pour sélectionner des colonnes dynamiquement:Les arguments de type pour la méthode Queryable.Select ne peuvent pas être déduites de l'utilisation

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

La genericFunc variable est censé définir le delegateType, mais je reçois toujours cette erreur . Qu'est-ce que je fais mal?

+1

Il est très difficile de vous aider sans plein exemple concret . (Je suggère d'utiliser 'List ' et 'AsQueryable' pour reproduire ceci.) –

+0

Ce qui est dans vos variables genericFunc, body et source .... – Milney

+0

La version non générique de' Expression.Lambda' ne peut pas être utilisée directement. –

Répondre

2

Je vais citer @Ivan Stoev

Le problème est que TResult besoins soient connus au moment de la compilation, donc linqQuery (doit) être IQueryable<string>, ou IQueryable<int> etc. Il est impossible de résoudre le type de var lors de l'exécution. Il est possible d'émettre l'appel à Select dynamiquement, mais tout ce que vous pouvez obtenir sera un IQueryable non générique, ce qui n'est pas très utile. Vous pouvez jeter un coup d'oeil au paquet DynamicLINQ pour voir comment il répond à ce problème et à d'autres problèmes similaires. Mais même avec cela, il est difficile de travailler avec IQueryable non typé.

Ensuite, si vous voulez vraiment le faire ... À la fin, il est votre code :-)

Cela peut être mis en mémoire cache:

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

Puis:

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

Notez que j'obtiens un IQueryable non-générique ... Puis je jette ses éléments à object et fais un ToArray(), mais vous pouvez faire ce que vous voulez avec. En dessous de la IQueryable sera fortement typé, donc ce sera un IQueryable<int> ou un IQueryable<something>, de sorte que vous pouvez jeter revenir à l'interface « réelle » (IQueryable<T> hérite de IQueryable)