Albero di espressione per Enumerable.Select

c# expression-trees

Domanda

Modifica Penso di poter chiedere meglio (nessun codice necessario in questo caso). Quindi la domanda in generale è: come usare un albero di espressioni per costruire una chiamata a un metodo generico (Select<TEntity,TResult> nel mio caso) quando TResult viene creato in fase di runtime? Ignora tutto il codice e il testo qui sotto, quella era la versione non chiara della domanda, lasciata per non confondere quelli che hanno risposto.

Ho bisogno di un esempio su come costruire un albero di espressioni per una chiamata "Seleziona". Il problema è che non conosco il tipo di risultato in fase di compilazione (può essere definito dall'utente tramite una GUI in runtime). Ecco un codice di come sto provando a fare questo (ricorda che non posso usare nessun generico)

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

Usando questa classe 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);

Quello che sto cercando di fare è creare un Type in runtime usando Reflection.Emit (metodo "CreateType" nel codice sopra, è usato in alcuni dei miei progetti e sembra a posto) e in qualche modo lo passa per chiamare "Select", ma Ottengo eccezione:

Nessun metodo generico 'Seleziona' nel tipo 'System.Linq.Enumerable' è compatibile con gli argomenti e gli argomenti del tipo fornito.

Eventuali suggerimenti?

Aggiornamenti il mio vero problema è la costruzione di un albero di espressione per una chiamata "Join" per i tipi di runtime che è più complicato, quindi ho deciso di chiedere "Seleziona", perché sto avendo la stessa eccezione nell'ultima riga (Expression.Call ( ...))

Upd2 includeva i miei metodi di supporto e modifica il codice principale, tuttavia questo non è il problema principale.

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

Risposta accettata

Puoi semplificare questo problema rimuovendo il tipo dinamico dall'equazione. È possibile riprodurre lo stesso problema con il codice sottostante, che fa esattamente la stessa cosa, ma senza il tipo dinamico.

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

Quindi il problema è la firma del metodo di CreateObject. Dal momento che il suo tipo di ritorno non è il tipo dinamico, il lambda non è valido. Puoi vedere questo cambiando il tipo di CreateObject .

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

Dato che hai a che fare con un tipo dinamico, devi creare un'espressione per trasmettere il risultato di CreateObject al tuo tipo dinamico. Prova a usare qualcosa come questo:

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


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché