Ausdrucksbaum für Enumerable.Select

c# expression-trees

Frage

Edit Ich denke, ich kann besser fragen (in diesem Fall ist überhaupt kein Code nötig). Die allgemeine Frage ist also: Wie (Select<TEntity,TResult> einen Ausdrucksbaum um einen Aufruf einer generischen Methode zu (Select<TEntity,TResult> in meinem Fall) wenn TResult zur Laufzeit erstellt wird? Ignorieren Sie den ganzen Code und Text unten, das war eine unklare Version der Frage, überließ es den nicht, die antworteten.

Ich brauche ein Beispiel, wie man eine Ausdrucksbaumstruktur für einen "Select" -Aufruf erstellt. Das Problem ist, dass ich den Ergebnistyp zur Kompilierzeit nicht kenne (er kann vom Benutzer über eine GUI in Runtime definiert werden). Hier ist ein Code, wie ich das versuche (erinnere mich daran, dass ich keine Generika verwenden kann)

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

Verwenden Sie diese Klasse in 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);

Was ich versuche, ist ein Typ zur Laufzeit mit Reflection.Emit (Methode "CreateType" im obigen Code, es wird in einigen meiner Projekte verwendet und sieht gut aus) und irgendwie übergeben, um an "Select", aber Ich bekomme eine Ausnahme:

Keine generische Methode 'Select' beim Typ 'System.Linq.Enumerable' ist mit den übergebenen Typargumenten und Argumenten kompatibel.

Irgendwelche Vorschläge?

Upd mein wirkliches Problem ist das Erstellen eines Ausdrucksbaums für einen "Join" -Aufruf für Laufzeittypen, der komplizierter ist, also entschied ich mich, nach "Select" zu fragen, weil ich dieselbe Ausnahme in der letzten Zeile (Expression.Call ( ...))

Upd2 enthielt meine Hilfsmethoden und den bearbeiteten Hauptcode , aber das ist nicht das Hauptproblem.

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

Akzeptierte Antwort

Sie können dieses Problem vereinfachen, indem Sie den dynamischen Typ aus der Gleichung entfernen. Sie können dasselbe Problem mit dem folgenden Code reproduzieren, der genau dasselbe tut, aber ohne den dynamischen Typ.

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

Das Problem ist also die Methodensignatur von CreateObject. Da sein Rückgabetyp nicht der dynamische Typ ist, ist das Lambda nicht gültig. Sie können dies sehen, indem Sie den Typ von CreateObject .

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

Da es sich um einen dynamischen Typ handelt, müssen Sie einen Ausdruck erstellen, um das Ergebnis von CreateObject auf Ihren dynamischen Typ zu übertragen. Verwenden Sie etwas wie folgt:

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


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum