使用匿名类型如何在.join()之后转换为lambda .Where()的表达式树? [英] How to do the convertion to Expression tree of lambda .Where() after .join() using anonymous type?

查看:47
本文介绍了使用匿名类型如何在.join()之后转换为lambda .Where()的表达式树?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在.Where()和.Select()中使用匿名类型来完成以下查询到表达式树语法的转换?

How to finish the convertion of the following query to Expression tree syntax, using an anonymous type in the .Where() and .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):

At this point I have (Thanks to @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():

For the .Where() I tryed:

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

我敢打赌,这在选择上也会有些诡异...

And I bet that it will also be some trickery in the select...

如何在.join()之后使用匿名类型转换为lambda .Where()到表达式树?

How to do the convertion to Expression tree of lambda .Where() using anonymous type after the .join()?

推荐答案

使用我的ExpressionExt class的这个缩小版本来添加扩展,以使Expression树的构建更加容易:

Using this minified version of my ExpressionExt class for adding extensions to make Expression tree building somewhat easier:

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), resultFne.ReturnType }, src, resultFne);

    public static MethodCallExpression Where(this Expression src, LambdaExpression predFne) => Expression.Call(TQueryable, "Where", new[] { src.TypeGenArg(0) }, src, 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, Expression body) => Expression.Lambda(body, 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树重新创建它:

And recreate it using an Expression tree with:

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

这篇关于使用匿名类型如何在.join()之后转换为lambda .Where()的表达式树?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆