Ekspresi Lambda menjadi Ekspresi <T> Tree (Slice 2 - Join)

Sep 10 2020

Bagaimana cara memasukkan gajah ke dalam lemari es? Anda mengirisnya ... Jadi, bagaimana Anda menerjemahkan kueri lambda ke pohon ekspresi? Anda mengirisnya ...

Ini adalah rangkaian 3 irisan ekspresi lambda ke pohon Ekspresi.

Slice 1. Di sini , Bagaimana melakukan .join () menggunakan contoh Microsoft.

Slice 2 (Slice Ini). Eager loading dengan parameter yang berbeda dari yang digunakan di Slice 1.

Slice 3. Di sini , Bagaimana menyelesaikan Query dengan .where () dan .Select ().

Dalam Slice ini saya mencoba untuk mengubah query berikut menjadi sintaks pohon ekspresi:

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

Dalam potongan 1 saya punya:

Contoh transformasi sebelumnya:

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

Itu menghasilkan jawaban @NetMage, yang melakukan trik dengan:

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

Untuk potongan ini, pernyataan berikut - disajikan di potongan sebelumnya - perlu diubah:

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

@NetMage mengadaptasikannya ke generik A dan B:

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

dan

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

menjadi (diadaptasi ke generik A dan B):

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

Sekarang diperlukan untuk memfilter hasil dengan b.Name dan Select a (Next Slice).

Jawaban

NetMage Sep 12 2020 at 03:44

Untuk mendapatkan tipe pengembalian dari Joinmetode ini, lakukan ini:

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

Anda perlu membuat tipe anonim di mana anggota Amemiliki tipe db.Aentitas tipe. Jika Anda menggunakan misalnya typeof(A), maka Anda akan memiliki Atipe Type, sama sekali tidak terkait dengan Join. (Jika Anda mengetahui jenis entitas A, Anda dapat menggunakan defaultseperti yang saya lakukan: mis . If db.Ais DbSet<AClass>then use A = default(AClass).)

Karena Anda tidak mereferensikan properti apa pun dalam Joinhasil, Anda cukup menggunakan parameter:

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