如何解决如何在使用匿名类型的.join之后转换为lambda .Where的表达式树?
如何在.Where()和.Select()中使用匿名类型完成以下查询到表达式树语法的转换?
IQueryable<A> As = db.A
.Join(
db.B,_a => _a.bID,_b => _b.ID,(a,b) => new { a,b })
//next two are the objective
.Where(s=> s.b.Name == "xpto")
.Select(s => s.a);
至此,我有了(感谢@NetMage):
//QUERY DB with GENERIC TYPE
IQueryable<B> queryable = (IQueryable<B>)db.B;
// IQueryable<TOuter>
var arg0 = Expression.Constant(db.A.AsQueryable());
// IEnumerable<TInner>
var arg1 = Expression.Constant(queryable);
// TOuter
var arg2p = Expression.Parameter(modelType2,"_a");
// TKey
var arg2body = Expression.PropertyOrField(arg2p,"_bID");
// also TKey
var arg2 = Expression.Lambda(arg2body,arg2p);
// TInner
var arg3p = Expression.Parameter(typeof(B),"_b");
// TKey
var arg3body = Expression.PropertyOrField(arg3p,"ID");
var arg3 = Expression.Lambda(arg3body,arg3p);
// TResult
var anonymousType = (new { a = db.A.FirstOrDefault(),b = db.B.FirstOrDefault() }).GetType();
// .ctor
var arg4Constructor = anonymousType.GetConstructors()[0];
//
var arg4A = arg2p;
// pet.Name
var arg4B =arg3p;
// Type array
var arg4Args = new[] { arg4A,arg4B };
var arg4Members = anonymousType.GetProperties();
var arg4body = Expression.New(arg4Constructor,arg4Args,arg4Members);
//
var arg4 = Expression.Lambda(arg4body,arg2p,arg3p);
MethodInfo joinGenericMI = typeof(Queryable)
.getmethods(BindingFlags.Static | BindingFlags.Public)
.Where(m => m.Name == "Join" && m.GetParameters().Length == 5)
.First();
var joinMI = joinGenericMI.MakeGenericmethod(new[] { arg2p.Type,arg3p.Type,arg2.ReturnType,anonymousType });
var qExpr = Expression.Call(joinMI,arg0,arg1,arg2,arg3,arg4);
对于我尝试过的.Where():
//.Where(s => s.b.Name == "xpto")
//s
ParameterExpression s = Expression.Parameter(anonymousType,"s");
//s.b
Expression left1 = Expression.Property(s,anonymousType.GetProperty("b"));
//s.b.Name
Expression left2 = Expression.Property(left1,"Name");
//"xpto"
Expression right = Expression.Constant("xpto",typeof(string));
//s.b.Name="xpto"
Expression e2 = Expression.Equal(left2,right);
ParameterExpression t = Expression.Parameter(typeof(string),"t");
//BLOCK WHERE
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),"Where",new Type[] { typeof(A) },qExpr,// Queryable with join
Expression.Lambda<Func<string,bool>>(e2,new ParameterExpression[] { t })); //'e2' is the where Expression,and 't' the input string for the comparison
//BLOCK WHERE
它引发类似以下内容的
InvalidOperation: No generic method "where" of type 'System.Linq.Queryable' is compatible with the arguments,and the type arguments. You should not give type arguments if it isn't a generic method (this is a rough translation of the error).
我敢打赌,这在选择中也会有些诡异...
如何在.join()之后使用匿名类型转换为lambda .Where()到表达式树?
解决方法
使用我的ExpressionExt
class
的缩小版来添加扩展名,以使Expression
树的构建更加容易:
public static class ValueTupleExt {
private static T[] makeArray<T>(params T[] itemArray) => itemArray;
public static T[] ToArray<T>(this (T,T) tuple) => makeArray(tuple.Item1,tuple.Item2);
}
public static class ExpressionExt {
private static Type TQueryable = typeof(Queryable);
private static Type TypeGenArg(this Expression e,int n) => e.Type.GetGenericArguments()[n];
public static MethodCallExpression Join(this Expression outer,Expression inner,LambdaExpression outerKeyFne,LambdaExpression innerKeyFne,LambdaExpression resultFne) =>
Expression.Call(TQueryable,"Join",new[] { outer.TypeGenArg(0),inner.TypeGenArg(0),outerKeyFne.ReturnType,resultFne.ReturnType },outer,inner,outerKeyFne,innerKeyFne,resultFne);
public static MethodCallExpression Select(this Expression src,LambdaExpression resultFne) => Expression.Call(TQueryable,"Select",new[] { src.TypeGenArg(0),src,resultFne);
public static MethodCallExpression Where(this Expression src,LambdaExpression predFne) => Expression.Call(TQueryable,"Where",new[] { src.TypeGenArg(0) },predFne);
public static ConstantExpression AsConst<T>(this T obj) => Expression.Constant(obj,typeof(T));
public static MemberExpression Dot(this Expression obj,string propNames) =>
(MemberExpression)propNames.Split('.').Aggregate(obj,(ans,propName) => Expression.PropertyOrField(ans,propName));
public static LambdaExpression Lambda(this ParameterExpression p1,Expression body) => Expression.Lambda(body,p1);
public static LambdaExpression Lambda(this (ParameterExpression,ParameterExpression) parms,parms.ToArray());
public static NewExpression New(this Type t,params Expression[] vals) => Expression.New(t.GetConstructors()[0],vals,t.GetProperties());
public static BinaryExpression opEq(this Expression left,Expression right) => Expression.Equal(left,right);
public static ParameterExpression Param(this Type t,string pName) => Expression.Parameter(t,pName);
}
您可以进行查询:
IQueryable<A> As = db.A
.Join(
db.B,_a => _a.bID,_b => _b.ID,(_a,_b) => new { a = _a,b = _b })
.Where(s => s.b.Name == "xpto")
.Select(s => s.a);
并使用带有以下内容的Expression
树重新创建它:
var aParm = typeof(A).Param("_a");
var aKeyFne = aParm.Lambda(aParm.Dot("bID"));
var bParm = typeof(B).Param("_b");
var bKeyFne = bParm.Lambda(bParm.Dot("ID"));
var anonType = (new { a = default(A),b = default(B) }).GetType();
var resultFne = (aParm,bParm).Lambda(anonType.New(aParm,bParm));
var join = db.A.AsConst().Join(db.B.AsConst(),aKeyFne,bKeyFne,resultFne);
var sParm = anonType.Param("s");
var predFne = sParm.Lambda(sParm.Dot("b.Name").opEq("xpto".AsConst()));
var where = join.Where(predFne);
var qexpr = where.Select(sParm.Lambda(sParm.Dot("a")));
IQueryable<A> AsE = new System.Linq.EnumerableQuery<A>(qexpr);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。