Espressione lambda in Expression <T> Tree (Slice 2 - Join)

Sep 10 2020

Come si mette un elefante in un frigorifero? Lo affetti ... Quindi, come si traduce una query lambda in albero delle espressioni? Taglialo ...

Questa è una serie di 3 fette di espressione lambda per l'albero delle espressioni.

Slice 1. Qui , Come fare .join () usando l'esempio Microsoft.

Fetta 2 (Questa Fetta). Caricamento desideroso con parametri diversi da quelli utilizzati in Slice 1.

Sezione 3. Qui , come terminare la query con .where () e .Select ().

In questa sezione sto cercando di convertire la seguente query nella sintassi dell'albero delle espressioni:

IQueryable<A> As = db.A
                     .Join(
                           db.B,
                           _a => _a.bID,
                           _b => _b.ID,
                           (a, b) => new { a, b })
                     //The next two statements are going to next slice.
                     .Where(s=> s.b.Name == "xpto")
                     .Select(s => s.a);

Nella fetta 1 ho avuto:

Precedente esempio di trasformazione:

var query = people.AsQueryable().Join(pets,
                person => person,
                pet => pet.Owner,
                (person, pet) =>
                    new { OwnerName = person.Name, Pet = pet.Name });

Ciò ha portato alla risposta di @NetMage, che ha funzionato con:

// Build Queryable.Join<TOuter,TInner,TKey,TResult> and use as query expression

// IQueryable<TOuter>
var arg0 = Expression.Constant(people.AsQueryable());

// IEnumerable<TInner>
var arg1 = Expression.Constant(pets);

// TOuter person
var arg2p = Expression.Parameter(people.GetType().GetGenericArguments()[0], "person");
// also TKey person
// Expression<Func<TOuter,TKey>>: person => person
var arg2 = Expression.Quote(Expression.Lambda(arg2p, arg2p));

// TInner pet
var arg3p = Expression.Parameter(pets.GetType().GetGenericArguments()[0], "pet");
// TKey pet.Owner
var arg3body = Expression.Property(arg3p, "Owner");
// Expression<Func<TInner,TKey>>: pet => pet.Owner
var arg3 = Expression.Quote(Expression.Lambda(arg3body, arg3p));

// TResult = typeof(new { string OwnerName , string Pet })
var anonymousType = (new { OwnerName = default(string), Pet = default(string) }).GetType();
// .ctor
var arg4Constructor = anonymousType.GetConstructors()[0];
// person.Name
var arg4PersonName = Expression.Property(arg2p, "Name");
// pet.Name
var arg4PetName = Expression.Property(arg3p, "Name");
var arg4Args = new[] { arg4PersonName, arg4PetName };
// new[] { .OwnerName, .Pet }
var arg4Members = anonymousType.GetProperties();
// new { OwnerName = person.Name, Pet = pet.Name }
var arg4body = Expression.New(arg4Constructor, arg4Args, arg4Members);
// Expression<Func<TOuter,TInner,TResult>>: (person,pet) => new { OwnerName = person.Name, Pet = pet.Name }
var arg4 = Expression.Quote(Expression.Lambda(arg4body, arg2p, arg3p));

var joinGenericMI = typeof(Queryable).GetMethod("Join", 5);
var joinMI = joinGenericMI.MakeGenericMethod(new[] { arg2p.Type, arg3p.Type, arg2.ReturnType, anonymousType });
var qExpr = Expression.Call(joinMI, arg0, arg1, arg2, arg3, arg4);

Per questa sezione, la seguente dichiarazione, presentata nella sezione precedente, doveva essere modificata:

// TResult = typeof(new { string OwnerName , string Pet })
    var anonymousType = (new { OwnerName = default(string), Pet = default(string) }).GetType();

@NetMage lo ha adattato ai generici A e B:

// TResult = typeof(new { A , B })
var anonymousType = (new { A = db.A.FirstOrDefault(), db.B = B.FirstOrDefault() }).GetType();

e

var arg4Constructor = anonymousType.GetConstructors()[0];
// person.Name
var arg4PersonName = Expression.Property(arg2p, "Name");
// pet.Name
var arg4PetName = Expression.Property(arg3p, "Name");

è diventato (adattato ai generici A e B):

// object A
var arg4A = arg2p;
// object B
var arg4B = arg3p;

Ora è necessario filtrare il risultato per b.Name e selezionare a (fetta successiva).

Risposte

NetMage Sep 12 2020 at 03:44

Per ottenere il tipo restituito dal Joinmetodo, eseguire questa operazione:

// TResult = typeof(new { A , B })
var anonymousType = (new { A = db.A.FirstOrDefault(), db.B = B.FirstOrDefault() }).GetType();

È necessario creare un tipo anonimo in cui il membro Aha un tipo di db.Aentità di tipo. Se usi ad esempio typeof(A), avrai Acon type Type, completamente estraneo al file Join. (Se conoscessi il tipo di entità di A, potresti semplicemente usarlo defaultcome ho fatto io: ad esempio if db.Ais DbSet<AClass>then use A = default(AClass).)

Dal momento che non stai facendo riferimento a nessuna proprietà nel Joinrisultato, usi solo i parametri:

// object A
var arg4A = arg2p;
// object B
var arg4B = arg3p;