在另一个lambda表达式中使用lambda表达式

时间:2014-10-17 10:37:05

标签: c# linq lambda expression

我需要组合两个lambda表达式,第二个表达式“包含”第一个表达式。 我搜索了很多,但没有找到任何明确的答案......

我想做的是以下内容: 第一个表达式“expression1”作为参数传递给方法,并且仅用于定义第二个lambda必须在哪个字段或属性上运行。

示意图,我正在尝试执行以下操作:

// simple field selector :
Expression<Func<T, string>> expression1 = obj => obj.field; 

// trying to use the field selector in 2nd expression :
Expression<Func<T, bool>> e2 = obj => [[Result of expression1]].Equals("myValue");

换句话说,我想得到:

Expression<Func<T, bool>> e2 = obj => obj.field.Equals("myValue"); 

我需要这样做,因为它并不总是被调用的Equals()方法,而是许多不同的方法。

我尝试将expression1编译为Func<T, string>以便在expression2中调用此Func,但是当我在LinQ中使用它时,我得到一个异常,因为LinQ不支持Invoke节点类型。

所以我的问题是:有没有办法将这两个表达式的主体结合起来,请问有多少?

先谢谢!

2 个答案:

答案 0 :(得分:3)

嗯,这应该可以解决问题:

void Main()
{
    var execute = Create<TestClass>(
            first => first.MyStringField, // field selector
            second => second.Equals("1234") // method selector
        );

    Console.WriteLine(execute(new TestClass("1234"))); // true
    Console.WriteLine(execute(new TestClass("4321"))); // false
}

class TestClass
{
    public string MyStringField;

    public TestClass(string val){
        MyStringField = val;
    }

}

static Func<TSource, bool> Create<TSource>(
                    Expression<Func<TSource, string>> fieldSelector,
                    Expression<Func<string, bool>> methodSelector
                )
{
    // todo: classical validation of fieldSelector, if necessary.
    // todo: classical validation of methodSelector, if necessary.

    var compiledFieldSelector = fieldSelector.Compile();
    var compiledMethodSelector = methodSelector.Compile();

    return T => compiledMethodSelector(compiledFieldSelector(T));
}

注意,由于lambda表达式的性质,你需要验证字段选择器和方法选择器,否则它可能会做一些非常奇怪的事情。

或者,下一个方法创建的lambda不会&#34;组成&#34;事实上,这是更好的,因为LinqToEntities在解释查询时不会有问题。

 static Func<TSource, bool> Create<TSource>(
                    Expression<Func<TSource, string>> memberSelector,
                    Expression<Func<string, bool>> methodSelector
                )
{
    // todo: classical validation of memberSelector, if necessary.
    // todo: classical validation of methodSelector, if necessary.

    var memberExpression = (MemberExpression)(memberSelector.Body);
    var methodCallExpression = (MethodCallExpression)(methodSelector.Body);

    // input TSource => ..
    var input = Expression.Parameter(typeof(TSource));

    var call = Expression.Call(
                Expression.MakeMemberAccess(
                                input, 
                                memberExpression.Member), 

                methodCallExpression.Method, 
                methodCallExpression.Arguments);

    return Expression.Lambda<Func<TSource, bool>>(call, input)
                .Compile();
}

答案 1 :(得分:1)

Chris Eelmaa的第二个答案是肯定的,只需删除.Compile()调用以避免出现Invoke异常:

static Expression<Func<TSource, bool>> Create<TSource>(
                    Expression<Func<TSource, string>> memberSelector,
                    Expression<Func<string, bool>> methodSelector
                )
{
    // todo: classical validation of memberSelector, if necessary.
    // todo: classical validation of methodSelector, if necessary.

    var memberExpression = (MemberExpression)(memberSelector.Body);
    var methodCallExpression = (MethodCallExpression)(methodSelector.Body);

    // input TSource => ..
    var input = Expression.Parameter(typeof(TSource));

    var call = Expression.Call(
                Expression.MakeMemberAccess(
                                input, 
                                memberExpression.Member), 

                methodCallExpression.Method, 
                methodCallExpression.Arguments);

    return Expression.Lambda<Func<TSource, bool>>(call, input);
}

在我的情况下,然后像这样使用: (selector是一个类似u => u.id的表达式 requestIQueryable<T>,都是作为参数收到的)

Expression<Func<T, bool>> contains = Create<T>(selector, u => u.Contains(searchExpression));
IQueryable<T> result = request.Where(contains);