【问题标题】:Lambda expression to Expression<T> Tree (Slice 2 - Join)Lambda 表达式到 Expression<T> 树(切片 2 - 连接)
【发布时间】:2020-12-28 12:16:57
【问题描述】:

如何将大象放入冰箱?你切片它...... Sooo,你如何将 lambda 查询转换为表达式树?你把它切片...

这是一系列 3 片 lambda 表达式到表达式树。

切片 1.Here,如何使用 Microsoft 示例执行 .join()。

切片 2(此切片)。使用与 Slice 1 中使用的参数不同的参数进行预加载。

Slice 3.Here,如何使用 .where() 和 .Select() 完成查询。

在这个 Slice 中,我尝试将以下查询转换为表达式树语法:

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 过滤结果并选择一个(下一个 Slice)。

【问题讨论】:

  • 你确定你使用了正确的术语,你的代码中没有表达式树,或者你之前的问题......你确定这是你想要的docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
  • 你是绝对正确的。这里没有表达式树......这正是问题所在......它会是什么(可能是凌晨 3:30 没有帮助解决问题):) @NetMage 在之前的工作中做得很好问题在哪里有一个需要什么的微软示例。他给出了一个很好的答案并指向 linqpad ......但我在填补这两种情况之间的空白时遇到了问题。我不想在这个问题上发布他的答案,但也许会很好......谢谢你的评论。
  • 我写了一个library 可以帮助你。添加 NuGet 包,添加 using ExpressionTreeToString;,然后调用 As.Expression.ToString("Factory methods", "C#"); 以返回一个字符串,其中包含重现给定表达式所需的工厂方法。或者,您可以安装我写的visualizerchoose the factory methods formatter
  • @ZevSpitz,那里真的很酷。确实很酷。我可以从用户的角度为图书馆提供输入吗?如果您还可以在可视化工具或字符串中进行逆操作——我的意思是:从 Linq 表达式(例如)创建表达式树——那将是非常棒的。附言喜欢可视化器:)
  • 在上一条评论中:“我的意思是:从一个 Linq 表达式(例如),在 C# 代码中创建表达式树”

标签: c# entity-framework linq lambda expression


【解决方案1】:

要从Join 方法获取返回类型,请执行以下操作:

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

您需要创建一个匿名类型,其中成员 A 的类型为 db.A 实体类型。如果你使用例如typeof(A),那么您将拥有A 类型为Type,与Join 完全无关。 (如果您知道A 的实体类型,您可以像我一样使用default:例如,如果db.ADbSet&lt;AClass&gt;,则使用A = default(AClass)。)

由于您没有在 Join 结果中引用任何属性,因此您只需使用参数:

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

【讨论】:

  • 我已编辑我的问题以反映您的回答。不久之后,我又被卡住了,现在在 .where() 中。我试图在 EDIT 2 中展示我所做的事情(可能是一些新手错误:-/)。
  • 我把这个问题一分为二。我正在使用 LinqPad(很棒,很棒的工具),但无法让 .Where() 工作。如果可以的话,你能检查一下吗?我正在积极尝试找出解决方案,但我没有能力做到这一点,而此刻你就是答案。谢谢。