Lambda-Ausdruck zum Ausdruck <T> -Baum (Slice 2 - Join)

Sep 10 2020

Wie stellt man einen Elefanten in einen Kühlschrank? Sie schneiden es ... Sooo, wie übersetzen Sie eine Lambda-Abfrage in einen Ausdrucksbaum? Sie schneiden es ...

Dies ist eine Serie von 3 Scheiben Lambda-Expression zum Expressionsbaum.

Slice 1. Hier erfahren Sie, wie Sie .join () anhand eines Microsoft-Beispiels ausführen.

Slice 2 (Dieses Slice). Eifriges Laden mit anderen Parametern als in Slice 1.

Slice 3. Hier , So beenden Sie die Abfrage mit .where () und .Select ().

In diesem Slice versuche ich, die folgende Abfrage in die Ausdrucksbaumsyntax zu konvertieren:

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

In Scheibe 1 hatte ich:

Vorheriges Transformationsbeispiel:

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

Das führte zu einer @ NetMage-Antwort, die den Trick machte mit:

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

Für dieses Slice musste die folgende Anweisung - die im vorherigen Slice dargestellt wurde - geändert werden:

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

@NetMage hat es an generisches A und B angepasst:

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

und

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

wurde (angepasst an generisches A und B):

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

Jetzt muss das Ergebnis nach b.Name gefiltert und a (nächstes Slice) ausgewählt werden.

Antworten

NetMage Sep 12 2020 at 03:44

Gehen Sie folgendermaßen vor, um den Rückgabetyp von der JoinMethode abzurufen:

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

Sie müssen einen anonymen Typ erstellen, bei dem das Mitglied Aden Typ db.AEntitätstyp hat. Wenn Sie zB verwenden typeof(A), dann haben Sie Amit Typ Type, völlig unabhängig von der Join. (Wenn Sie wusste , dass die Entitätstyp A, man konnte einfach verwenden defaultwie ich: zB wenn db.Asich DbSet<AClass>dann verwenden A = default(AClass).)

Da Sie im JoinErgebnis keine Eigenschaften referenzieren , verwenden Sie einfach die folgenden Parameter:

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