Biểu thức Lambda thành Biểu thức <T> Cây (Lát 2 - Tham gia)

Sep 10 2020

Làm thế nào để bạn đặt một con voi vào tủ lạnh? Bạn cắt nó ... Sooo, làm thế nào để bạn Dịch một cây truy vấn lambda sang biểu thức? Bạn cắt nó ...

Đây là một loạt 3 lát của biểu thức lambda đến cây Biểu thức.

Slice 1. Đây là cách thực hiện .join () bằng ví dụ của Microsoft.

Lát 2 (Lát này). Đang tải háo hức với các thông số khác với các thông số được sử dụng trong Lát 1.

Slice 3. Ở đây , Cách kết thúc Truy vấn với .where () và .Select ().

Trong Slice này, tôi đang cố gắng chuyển đổi truy vấn sau sang cú pháp cây Biểu thức:

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

Trong phần 1, tôi có:

Ví dụ trước về chuyển đổi:

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

Điều đó dẫn đến câu trả lời @NetMage, điều đó đã xảy ra với:

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

Đối với phần này, câu lệnh sau - được trình bày trong phần trước - cần được thay đổi:

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

@NetMage đã điều chỉnh nó thành A và B chung:

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

và

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

đã trở thành (thích nghi với A và B chung):

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

Bây giờ nó là cần thiết để lọc kết quả theo b.Name và Chọn a (Slice tiếp theo).

Trả lời

NetMage Sep 12 2020 at 03:44

Để lấy kiểu trả về từ Joinphương thức, hãy làm như sau:

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

Bạn cần tạo một kiểu ẩn danh trong đó thành viên Acó kiểu db.Athực thể kiểu. Nếu bạn sử dụng vd typeof(A), thì bạn sẽ có Avới loại Type, hoàn toàn không liên quan đến Join. (Nếu bạn biết loại thực thể của A, bạn có thể sử dụng defaultgiống như tôi đã làm: ví dụ: if db.Ais DbSet<AClass>then use A = default(AClass).)

Vì bạn không tham chiếu đến bất kỳ thuộc tính nào trong Joinkết quả, bạn chỉ cần sử dụng các tham số:

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