Лямбда-выражение в Expression <T> Tree (Slice 2 - Join)

Sep 10 2020

Как положить слона в холодильник? Вы его разрезаете ... Так как же перевести лямбда-запрос в дерево выражений? Вы его режете ...

Это серия из 3 частей лямбда-выражения для дерева выражений.

Часть 1. Здесь , как сделать .join () на примере Microsoft.

Срез 2 (этот срез). Активная загрузка с параметрами, отличными от тех, которые использовались в слайсе 1.

Часть 3. Здесь показано, как завершить запрос с помощью .where () и .Select ().

В этом фрагменте я пытаюсь преобразовать следующий запрос в синтаксис дерева выражений:

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

В срезе 1 у меня было:

Предыдущий пример трансформации:

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

Это привело к ответу @NetMage, который помог:

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

Для этого фрагмента необходимо изменить следующий оператор, представленный в предыдущем фрагменте:

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

@NetMage адаптировал его к общим A и B:

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

и

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

стал (адаптирован к родовым A и B):

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

Теперь нужно отфильтровать результат по b.Name и Select a (next Slice).

Ответы

NetMage Sep 12 2020 at 03:44

Чтобы получить тип возвращаемого значения из Joinметода, сделайте следующее:

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

Вам необходимо создать анонимный тип, член которого Aимеет тип db.Aсущности. Если вы используете, например typeof(A), то у вас будет Aтип with Type, совершенно не связанный с Join. (Если бы вы знали тип сущности A, вы могли бы просто использовать, defaultкак я: например, if db.Ais DbSet<AClass>then use A = default(AClass).)

Поскольку вы не ссылаетесь на какие-либо свойства в Joinрезультате, вы просто используете параметры:

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