在select中提取lambda表达式

时间:2014-02-07 17:31:25

标签: c# lambda linq-to-entities expression expression-trees

我可以像这样提取和重用整个表达式:

Expression<Func<User, int>> userExpression = x => x.Roles.Count()

但是有可能只提取一些x.Roles.Count()部分,并在Expression<Func<User, T>>

的上下文中使用它

我想要实现的是在不同选择中重用该部分,如:

users.Select(x => new AnotherClass { RoleCount = roleCountPartOfExpression})

users.Select(x => new OneMoreAnotherClass 
              { 
                 AnotherProperty = roleCountPartOfExpression
              });

那么在这种情况下{@ 1}}应该在LINQ to Entities中支持(因此创建一个方法,我将传递用户roleCountPartOfExpression将无法工作)我也无法创建像return user.Roles.Count()这样的选择的表达式,因为在这种情况下我需要创建Expression<Func<User, AnotherClass>>,这将打破我的“可重用性”目标。

3 个答案:

答案 0 :(得分:4)

如果您编译为Func<User, int>,则可以在其他区域调用它:

Expression<Func<User, int>> userExpression = x => x.Roles.Count();

Func<User,int> userFunc = userExpression.Compile();

users.Select(x => new AnotherClass { RoleCount = userFunc(x) });

或者只是定义为Func开头:

Func<User,int> userFunc = x => x.Roles.Count();

这是使用Linq-to-Objects还是别的?如果您需要将其保留为Expression,因为Expression会转换为其他内容(如SQL调用),您可以使用LinqKitAsExpandable,如下所示:

public static Expression<Func<User,int>> RoleCount()
{
    return u => u.Roles.Count();
}

public static void DoStuff()
{
    var roleCounter = RoleCount();

    var query = users.AsExpandable()
                     .Select(u => roleCounter.Invoke(u));
}

答案 1 :(得分:1)

我们可以创建一个Combine方法,它能够为一个对象选择一个选择器,然后另一个选择器也可以获取第一个选择器的输出以产生最终结果:

public static Expression<Func<TFirstParam, TResult>>
    Combine<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], param)
        .Replace(second.Parameters[1], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

这使用以下帮助器方法将一个表达式的所有实例替换为另一个:

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

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

现在我们可以写:

Expression<Func<User, int>> userExpression = x => x.Roles.Count()

var query = users.Select(userExpression.Combine((x, count) => 
    new OneMoreAnotherClass { AnotherProperty = count});

答案 2 :(得分:-1)

你可以用一个闭包来做到这一点:

User x = z; // assign local value 
var countX = () => x.Roles.Count();

现在这将有效:

users.Select(x => new AnotherClass { RoleCount = countX() })