将两个LINQ的拉姆达前pressions [英] Combine two Linq lambda expressions

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

问题描述

Expression<Func<MyObject, string>> fn1 = x => x.PossibleSubPath.MyStringProperty;

Expression<Func<string, bool>> fn2 = x => x.Contains("some literal");

有没有一种方法来创建一个新的lambda前$ P $,基本上采用的fn1的输出,并使用它作为FN2输入pssion?

Is there a way to create a new lambda expression which basically uses the output of fn1 and uses it as input for fn2?

Expression<Func<MyObject, bool>> fnCombined = ...

我知道我可以一次创建功能,但问题是,我正在做一些通用的code,因此真正需要的是能够分别创建这两个功能,那么在这样的方式将它们组合该LINQ的可以在我的数据库对象(实体框架)中使用它们。

I know that I can create the function at once, but the problem is that I'm making some generic code and therefore really need to be able to create these two functions separately, then combine them in such a way that Linq can use them on my database objects (Entity Framework).

推荐答案

所以,逻辑上我们希望能够做的是建立一个新的lambda在它具有输入到第一个函数的参数,以及一个机构,调用与该参数的第一功能,然后将结果传递作为参数到所述第二功能,然后返回

So logically what we want to be able to do is create a new lambda in which it has a parameter of the input to the first function, and a body that calls the first function with that parameter and then passes the result as the parameter to the second function, and then returns that.

我们可以复制,可以轻松地使用足够防爆pression 对象:

We can replicate that easily enough using Expression objects:

public static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
    Expression<Func<T1, T2>> first,
    Expression<Func<T2, T3>> second)
{
    var param = Expression.Parameter(typeof(T1), "param");
    var body = Expression.Invoke(second, Expression.Invoke(first, param));
    return Expression.Lambda<Func<T1, T3>>(body, param);
}

可悲的是,EF和其他大多数查询提供不会真正知道该怎么做这一点,并不能正常工作。每当他们打了调用前pression他们一般只是把某种异常。部分的可以的,虽然处理。从理论上讲他们需要的所有信息是存在的,如果他们与稳健性写入得到它。

Sadly, EF and most other query providers won't really know what to do with that and won't function properly. Whenever they hit an Invoke expression they generally just throw an exception of some sort. Some can handle it though. In theory all the information they need is there, if they're written with the robustness to get at it.

我们所能做的不过是,从概念的角度来看,更换的拉姆达与我们正在创造一个新的lambda的参数体的第一个拉姆达的参数的每一个实例,然后替换第二拉姆达的参数的所有实例第二拉姆达与第一拉姆达的新机构。从技术上讲,如果这些前pressions有副作用,而这些参数使用一次以上,他们不会是相同的,但由于这些要由他们真的不应该永远有EF查询提供解析副作用。

What we can do however is, from a conceptual standpoint, replace every instance of the first lambda's parameter in that lambda's body with the parameter of a new lambda we're creating, and then replace all instances of the second lambda's parameter in the second lambda with the new body of the first lambda. Technically, if these expressions have side effects, and these parameters are used more than once, they wouldn't be the same, but as these are going to be parsed by an EF query provider they really shouldn't ever have side effects.

感谢David B中提供的链接,此相关的问题它提供了一个 ReplaceVisitor 的实施。我们可以使用 ReplaceVisitor 要经过一个前pression的整个树,并用另一个替换一个EX pression。这种类型的实现是:

Thanks to David B for providing a link to this related question which provides a ReplaceVisitor implementation. We can use that ReplaceVisitor to go through the entire tree of an expression and replace one expression with another. The implementation of that type is:

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

现在我们可以写我们的适当 联合方法:

public static Expression<Func<T1, T3>> Combine<T1, T2, T3>(
    Expression<Func<T1, T2>> first,
    Expression<Func<T2, T3>> second)
{
    var param = Expression.Parameter(typeof(T1), "param");

    var newFirst = new ReplaceVisitor(first.Parameters.First(), param)
        .Visit(first.Body);
    var newSecond = new ReplaceVisitor(second.Parameters.First(), newFirst)
        .Visit(second.Body);

    return Expression.Lambda<Func<T1, T3>>(newSecond, param);
}

和一个简单的测试情况下,只证明这是怎么回事:

and a simple test case, to just demonstrate what's going on:

Expression<Func<MyObject, string>> fn1 = x => x.PossibleSubPath.MyStringProperty;
Expression<Func<string, bool>> fn2 = x => x.Contains("some literal");

var composite = Combine(fn1, fn2);

Console.WriteLine(composite);

这将打印出:

参数=> param.PossibleSubPath.MyStringProperty.Contains(一些文字)

param => param.PossibleSubPath.MyStringProperty.Contains("some literal")

这就是我们想要的东西;查询供应商将知道如何解析类似的东西。

Which is exactly what we want; a query provider will know how to parse something like that.

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

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