合并两个LINQ表达式 [英] Merge two linq expressions

查看:212
本文介绍了合并两个LINQ表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有是在不同的时间建成了两个表达式,但需要为了得到一个准确的where子句中的分组合并。我曾尝试选项,但我使用实体框架和它doesn'不懂的调用功能。我见过一些在 ExpressionVisitor 替代的,但我不认为我有什么,我需要做好足够的了解。



如果任何人都可以请点我在正确的方向我将十分感激,那感觉就像是接近那里。



Where子句1A (对象类型表达式来; Func键< T,BOOL>>

  {PARM => parm.Name.Contains(帽子)} 



Where子句1B (对象类型 LambdaExpression ,但可以使用(表达式来; Func键< T,BOOL>>)LambdaExpression

  {PARM => parm.Attributes.Any(PARM =>((parm.Name ==测试)AndAlso运算(parm.Value ==21)))} 

需要的地方条款

  {PARM = GT; 
parm.Name.Contains(帽子)(安培;&安培; / ||)
parm.Attributes.Any(PARM =>((parm.Name ==测试)AndAlso运算( parm.Value ==21)))
}

如果有人可以请帮助我合并的 Where子句1A Where子句1B ,我会非常感激。



只是一个供参考在哪里()和任何()子句从IQueryable的,不知道如果该事项获得泛型方法

  Func键< MethodInfo的,布尔> methodLambda = M => m.Name ==任何&放大器;&安培; m.GetParameters()==长度2。 
MethodInfo的方法= typeof运算(可查询).GetMethods(),其中(methodLambda)。单()MakeGenericMethod(ActualQueryableType)。

ParameterExpression parentMember = Expression.Parameter(typeof运算(T),parentParm);

//创建任何()的表达式树并将其转换为拉姆达
MethodCallExpression callAny = Expression.Call(方法,成员,EXP);
LambdaExpression lambdaAny = Expression.Lambda(callAny,参数);


VAR combExp = parentExp.And((表达式来; Func键< T,BOOL>>)lambdaAny);


MethodCallExpression whereCall = Expression.Call(typeof运算(可查询),去哪儿,新类型[] {} query.ElementType,新的表达式[] {
query.Expression ,
Expression.Quote(combExp)
});

=查询(IQueryable的< T>)query.Provider.CreateQuery(whereCall);



使用,当我得到的错误调用




引发NotSupportedException



LINQ表达式节点类型调用不支持在LINQ到实体。



解决方案

下面的的实施PredicateBuilder 不使用调用

 公共静态类PredicateBuilder 
{
公共静态表达式来; Func键< T,BOOL>>真< T>(){返回F =>真正; }
公共静态表达式来; Func键< T,BOOL>>假< T>(){返回F =>假; }

公共静态表达式来; Func键< T,BOOL>>或LT; T>(
这个表达式来; Func键< T,BOOL>>表达式1,
表达式来; Func键< T,BOOL>>表达式2)
{
VAR secondBody = expr2.Body.Replace(expr2.Parameters [0],expr1.Parameters [0]);
返回Expression.Lambda<&Func键LT; T,BOOL>>
(Expression.OrElse(expr1.Body,secondBody),expr1.Parameters);
}

公共静态表达式来; Func键< T,BOOL>>和< T>(
这个表达式来; Func键< T,BOOL>>表达式1,
表达式来; Func键< T,BOOL>>表达式2)
{
VAR secondBody = expr2.Body.Replace(expr2.Parameters [0],expr1.Parameters [0]);
返回Expression.Lambda<&Func键LT; T,BOOL>>
(Expression.AndAlso(expr1.Body,secondBody),expr1.Parameters);
}
}



相反,它使用了替换办法(下实现),用于替换一个表达式的所有​​实例与其他。

 公共静态表达式替换(这个表达式的表情,
表达searchEx,表达replaceEx)
{
返回新ReplaceVisitor(searchEx,replaceEx).Visit(表达);
}

内部类ReplaceVisitor:ExpressionVisitor
{
从,私人只读表达;
公共ReplaceVisitor(从表达,表达于)
{
this.from =距离;
this.to =来;
}
公众覆盖式访问(Expression节点)
{
返回节点==而来?于:base.Visit(节点);
}
}



使用这个,你现在可以使用来,共同两块采取同样的输入谓词表达式。


I have two expressions that are built out at separate times, but need to be merged in order to get an accurate 'grouping' of a where clause. I did try this option, but I am using Entity Framework and it doesn't understand the Invoke function. I have seen some of the ExpressionVisitor alternative but I do not think I have a good enough understanding of what I need to do.

If anyone could please point me in the right direction I would much appreciate it, it feels like it is close to there.

Where Clause 1A (object type Expression<Func<T, bool>>)

{parm => parm.Name.Contains("hat")}

Where Clause 1B (object type LambdaExpression, but can use (Expression<Func<T, bool>>)LambdaExpression )

{parm => parm.Attributes.Any(parm => ((parm.Name == "test") AndAlso (parm.Value == "21")))}

Needed Where Clause

{parm =>
 parm.Name.Contains("hat") (&&/||)
 parm.Attributes.Any(parm => ((parm.Name == "test") AndAlso (parm.Value == "21")))
}

If someone could please help me merge Where Clause 1A and Where Clause 1B, I would be very thankful..

Just an FYI Where() and Any() clause are generic methods obtained from IQueryable, not sure if that matters.

Func<MethodInfo, bool> methodLambda = m => m.Name == "Any" && m.GetParameters().Length == 2;
MethodInfo method = typeof(Queryable).GetMethods().Where(methodLambda).Single().MakeGenericMethod(ActualQueryableType);

ParameterExpression parentMember = Expression.Parameter(typeof(T), "parentParm");

 // Create Any() Expression tree and convert it to lambda
MethodCallExpression callAny = Expression.Call(method, member, exp);
LambdaExpression lambdaAny = Expression.Lambda(callAny, param);


var combExp = parentExp.And((Expression<Func<T, bool>>)lambdaAny);


MethodCallExpression whereCall = Expression.Call(typeof(Queryable), "Where", new Type[] { query.ElementType }, new Expression[] {
query.Expression,
Expression.Quote(combExp)
});

query = (IQueryable<T>)query.Provider.CreateQuery(whereCall);

The error I get when using Invoke is:

NotSupportedException

The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

解决方案

Here's an implementation of PredicateBuilder that doesn't use Invoke:

public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(
        this Expression<Func<T, bool>> expr1,
        Expression<Func<T, bool>> expr2)
    {
        var secondBody = expr2.Body.Replace(expr2.Parameters[0], expr1.Parameters[0]);
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
    }
}

It instead uses a Replace method (implementation below) that replaces all instances of one expression with another.

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

Using this you can now use And to AND together two predicate expressions that take the same input.

这篇关于合并两个LINQ表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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