C# 如何编写linq.join和表达式树来进行动态查询

C# 如何编写linq.join和表达式树来进行动态查询,c#,asp.net,linq,lambda,expression,C#,Asp.net,Linq,Lambda,Expression,我似乎在任何地方都找不到答案 目标是找到一种使用表达式树语句执行linq.join()的方法 所以。。。使用: 班级人员 { 公共字符串名称{get;set;} } 等级宠物 { 公共字符串名称{get;set;} 公共人物所有者{get;set;} } 公共静态void JoinEx1() { Person magnus=新人{Name=“Hedlund,magnus”}; Person terry=新人{Name=“Adams,terry”}; Person charlotte=新人{Nam

我似乎在任何地方都找不到答案

目标是找到一种使用表达式树语句执行linq.join()的方法

所以。。。使用:

班级人员
{
公共字符串名称{get;set;}
}
等级宠物
{
公共字符串名称{get;set;}
公共人物所有者{get;set;}
}
公共静态void JoinEx1()
{
Person magnus=新人{Name=“Hedlund,magnus”};
Person terry=新人{Name=“Adams,terry”};
Person charlotte=新人{Name=“Weiss,charlotte”};
宠物大麦=新宠物{Name=“大麦”,所有者=特里};
宠物靴=新宠物{Name=“boots”,所有者=特里};
宠物胡须=新宠物{Name=“胡须”,所有者=夏洛特};
宠物雏菊=新宠物{Name=“daisy”,主人=马格纳斯};
名单人物=新名单{magnus,terry,charlotte};
宠物名单=新名单{大麦、靴子、胡须、雏菊};
//加入人物对象列表和宠物对象列表
//创建每个元素所在的个人宠物对列表
//一个匿名类型,包含宠物的名字和宠物的名字
//宠物主人的名字。
var query=people.AsQueryable().Join(宠物、,
person=>person,
宠物=>pet.Owner,
(人、宠物)=>
新的{OwnerName=person.Name,Pet=Pet.Name});

有谁能帮助我如何使用执行.join()吗?

由于
Queryable.join
是一种通用的静态方法,因此使用扩展方法查找所需的
MethodInfo
最简单:

public static class TypeExt {
    public static MethodInfo GetMethod(this Type t, string methodName, int paramCount) =>
        t.GetMethods().Where(mi => mi.Name == methodName && mi.GetParameters().Length == paramCount).Single();
}
Join
方法采用五个参数(扩展方法将应用它们的对象作为第一个参数传递),因此我们将它们构建为
表达式
一次一个。这五个参数是
IQueryable
、要联接的
IEnumerable
、外部键选择器lambda、内部键选择器lambda和结果选择器lambda

// 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));
最后,您可以使用
表达式创建
IQueryable

var q2 = people.AsQueryable().Provider.CreateQuery(qExpr);

对于这类事情非常有用-您可以使用
query.Expression.Dump()
输出编译器创建的
表达式
树,并确定如何自己构建它。在这种情况下,您使用的匿名类型有点棘手,但可以使用类型推断和匿名类型模板来处理。您可能还想看一看谓词生成器:是否要传入传递到
的参数将加入
,包括结果lambda,还是您也要生成密钥和结果lambda?@NetMage,所有内容。我必须从划痕创建它。参数、变量、它们之间的关系…还不能看到LINQPad,但可以。@NetMage,已安装的LINQPad…仍在尝试理解它的树输出:)THX joinGenericMI在5上引发了一个错误,我用MethodInfo joinGenericMI=typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)重播了它,其中(m=>m.Name==“Join”&&m.GetParameters().Length==5)。First();除此之外,它完全符合创建的目的。谢谢。@diogoalmeda有趣-您使用的是.Net的哪个版本?我已经安装了.Net v4.8(regedit中的版本528040)。@NetMageI已经尝试这个示例好几天了。对“arg2.ReturnType”的引用在MakeGenericMethod调用中,由于UnaryExpression不包含返回类型,因此会引发错误。有任何建议吗?@RachelStrander您是对的,这是一个错误:它应该是
arg2p.type
(尽管从概念上讲它是
arg2.ReturnType
,但由于lambda周围的
表达式.Quote
,它并不容易获得)。
var joinGenericMI = typeof(Queryable).GetMethod("Join", 5);
var joinMI = joinGenericMI.MakeGenericMethod(new[] { arg2p.Type, arg3p.Type, arg2p.Type, anonymousType });
var qExpr = Expression.Call(joinMI, arg0, arg1, arg2, arg3, arg4);
var q2 = people.AsQueryable().Provider.CreateQuery(qExpr);