Sélection énumérable par arbre d'expression

c# enumerable expression expression-trees lambda

Question

J'étudie "Expression Tree" mais je n'arrive pas à interpréter ces expressions:

// first case
someList.Select(p => p.SomeProperty);

et

// second case
someList.Select(p => new OtherClass 
{
    SomeProperty = p.SomeProperty
})

Pour le "premier cas" j'ai essayé de faire ceci:

var someList = new List<SomeClass>();
someList.Add(new SomeClass { SomeProperty = "Hello" });

var someParam = Expression.Parameter(typeof(SomeClass), "p");
var someProperty = Expression.Property(someParam, "SomeProperty");

Expression.Call(
    typeof(Enumerable),
    "Select",
    new Type[]
    {
        typeof(SomeClass),
        typeof(string)
    },
    Expression.Lambda(
        someProperty,
        someParam
    )
).Dump();

Mais je reçois cette erreur:

InvalidOperationException: aucune méthode générique 'Sélectionnez' sur le type 'System.Linq.Enumerable' n'est compatible avec les arguments de type et les arguments fournis. Aucun argument de type ne doit être fourni si la méthode est non générique.

À propos du "deuxième cas", je ne sais pas comment procéder.

Quelqu'un peut-il me guider ici?

Réponse acceptée

Calmez les gens, après quelques recherches, j'ai trouvé ce qui manquait dans mon code ...

Sur le premier cas:

Expression.Call(
    typeof(Enumerable),
    "Select",
    new Type[]
    {
        typeof(SomeClass),
        typeof(string)
    },
    Expression.Constant(someList), // <---------------- HERE IT IS
    Expression.Lambda(
        someProperty,
        someParam
    )
);

Dans le second cas, j'ai créé la "nouvelle" expression à l'aide du code ci-dessous:

var bind = Expression.Bind(typeof(OtherClass).GetProperty("SomeProperty"), someProperty);
var otherClassNew = Expression.New(typeof(OtherClass));
var otherClassInit = Expression.MemberInit(otherClassNew, bind);

Quoi qu'il en soit, merci à tous pour votre aide!


Réponse populaire

Quelques exemples de ce que vous pourriez faire:

Donné

public class SomeClass
{
    public string SomeProperty { get; set; }
}

et

var someList = new List<SomeClass>();
someList.Add(new SomeClass { SomeProperty = "Hello" });

var someParam = Expression.Parameter(typeof(SomeClass), "p");
var someProperty = Expression.Property(someParam, "SomeProperty");

Expression<Func<SomeClass, string>> lambda = Expression.Lambda<Func<SomeClass, string>>(someProperty, someParam); // p => p.SomeProperty

Utilisation d'un IEnumerable<SomeClass> ... Notez le .Compile()

Func<SomeClass, string> compiled = lambda.Compile();
IEnumerable<string> q1 = someList.Select(compiled);

Vous ne devriez jamais utiliser AsQueryable() mais dans des tests unitaires et des programmes "d'expérimentation" (comme celui-ci). Juste pour faire plaisir à Peter, je vais ajouter une autre condition possible: si vous savez vraiment ce que ça fait (ne pensez pas savoir ce que ça fait, vraiment !), Alors vous pouvez l'utiliser. Mais si vous l'utilisez pour la première fois, je vous suggère quand même de demander à SO si vous avez raison de l'utiliser.

IQueryable<SomeClass> queryable = someList.AsQueryable();

Directement en utilisant le Queryable.Select()

IQueryable<string> q2 = queryable.Select(lambda);

Construire un Select et utiliser CreateQuery (c'est très similaire à ce que fait en interne Queryable.Select ) pour "l'injecter" dans la requête.

MethodInfo select = (from x in typeof(Queryable).GetMethods()
                    where x.Name == "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().MakeGenericMethod(typeof(SomeClass), typeof(string));

MethodCallExpression select2 = Expression.Call(null, select, Expression.Constant(queryable), lambda);

IQueryProvider provider = queryable.Provider;
IQueryable<string> q3 = provider.CreateQuery<string>(select2);



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi