Arbre d'expression pour Enumerable.Select

c# expression-trees

Question

Edit Je pense que je peux demander mieux (aucun code nécessaire dans ce cas). La question générale est donc la suivante: comment utiliser un arbre d’expression pour créer un appel à une méthode générique (Select<TEntity,TResult> dans mon cas) lorsque TResult est créé à l’exécution? Ignorer tout le code et le texte ci-dessous, cette version incertaine de la question laissait le soin de ne pas confondre ceux qui avaient répondu.

J'ai besoin d'un exemple pour créer un arbre d'expression pour un appel "Select". Le problème est que je ne connais pas le type de résultat au moment de la compilation (il peut être défini par l'utilisateur via une interface graphique au moment de l'exécution). Voici un code de la façon dont j'essaie de faire cela (rappelez-vous que je ne peux utiliser aucun générique)

class Pet {
    public int Id { get; set; }
    public string Name { get; set; }
}

Utilisation de cette classe dans Main:

List<Pet> list = new List<Pet>();                
Expression eList = Expression.Constant(list);
ParameterExpression pe = Expression.Parameter(typeof(Pet), "p");
MethodInfo method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
Expression selectorBody = Expression.Call(method, Expression.Constant(properties));
Expression selector = Expression.Lambda(selectorBody, pe);
Expression res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector);

Ce que j'essaie de faire est de créer un type au moment de l'exécution à l'aide de Reflection.Emit (méthode "CreateType" dans le code ci-dessus, il est utilisé dans certains de mes projets et semble correct) Je reçois une exception:

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.

Aucune suggestion?

Mettre à jour mon vrai problème est de construire un arbre d’expression pour un appel "Join" pour les types d’exécution, ce qui est plus compliqué; ...))

Upd2 a inclus mes méthodes d'assistance et le code principal modifié, mais ce n'est pas le problème principal.

static Type CreateType(IEnumerable<PropertyInfo> properties) {
        TypeBuilder typeBuilder = CreateTypeBuilder("ResultDynamicAssembly", "ResultModule", "ResultType");
        foreach (PropertyInfo propertyInfo in properties) {
            CreateAutoImplementedProperty(typeBuilder, propertyInfo.Name, propertyInfo.PropertyType);
        }
        return typeBuilder.CreateType();
    }

    static object CreateObject(IEnumerable<PropertyInfo> properties) {
        Type type = CreateType(properties); 
        return Activator.CreateInstance(type);
    }

Réponse acceptée

Vous pouvez simplifier ce problème en supprimant le type dynamique de l'équation. Vous pouvez reproduire le même problème avec le code ci-dessous, qui fait exactement la même chose, mais sans le type dynamique.

static class Program
{
    private static void Main(string[] args)
    {
        var list = new List<Pet>();
        var eList = Expression.Constant(list);
        var pe = Expression.Parameter(typeof(Pet), "p");
        var method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
        var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
        var selectorBody = Expression.Call(method, Expression.Constant(properties));
        var selector = Expression.Lambda(selectorBody, pe);
        var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector);
    }

    private static Type CreateType(IEnumerable<PropertyInfo> properties)
    {
        return typeof (DynamicType);
    }

    private static object CreateObject(IEnumerable<PropertyInfo> properties)
    {
        var type = CreateType(properties);
        return  Activator.CreateInstance(type);
    }

    class Pet
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    class DynamicType
    {
        public string Name { get; set; }
    }
}

Le problème est donc la signature de méthode de CreateObject. Puisque son type de retour n'est pas le type dynamique, le lambda n'est pas valide. Vous pouvez le voir en changeant le type de CreateObject .

    // this works fine
    private static DynamicType CreateObject(IEnumerable<PropertyInfo> properties)
    {
        var type = CreateType(properties);
        return  (DynamicType) Activator.CreateInstance(type);
    }

Comme vous avez affaire à un type dynamique, vous devez créer une expression pour transtyper le résultat de CreateObject dans votre type dynamique. Essayez d'utiliser quelque chose comme ça:

        // create dynamic type
        var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user
        var dynamicType = CreateType(properties);

        // build expression
        var list = new List<Pet>();
        var eList = Expression.Constant(list);
        var pe = Expression.Parameter(typeof(Pet), "p");
        var createObjectMethod = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic);
        var createObjectCall = Expression.Call(createObjectMethod, Expression.Constant(properties));
        var castExpression = Expression.Convert(createObjectCall, dynamicType);
        var selectorExpression = Expression.Lambda(castExpression, pe);
        var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), dynamicType }, eList, selectorExpression);


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow