【问题标题】:Combining 2 LambdaExpressions into 1 LamdaExpression将 2 个 LambdaExpression 组合成 1 个 LamdaExpression
【发布时间】:2011-07-12 21:15:01
【问题描述】:

我正在动态生成要发送到 LINQ to Entities 的表达式树。我提供了一个框架,我允许开发人员将输出列指定为 lambda 表达式。

例如,假设他们有一个列,他们可以指定从数据库中提取值的方式是:

p => p.Aliases.Count()

另一列是:

p => p.Names.Where(n => n.StartsWith("hello"))

这里的问题是我需要将这两个组合成一个表达式。

我的第一次尝试是:

getter = field.SelectorExpression.Body;

我收到错误消息The parameter 'p' was not bound in the specified LINQ to Entities query expression,因为LINQ 无法知道p.Aliases.Count() 中的P 是什么。

接下来我尝试的是:

getter = Expression.Invoke(field.OrderBySelector, new[] {parameter});

但是,我收到了The LINQ expression node type 'Invoke' is not supported in LINQ to Entities. 的消息,这也是因为我不希望 SQL Server 知道如何运行 lambda 表达式。

现在基本都是这样的表达方式:

item => new MyClass
{ 
    SomeValue = item.Name, // this was generated from some other code
    AnotherValue = item.SomeOtherColumn, // there can be lots of these
    AliasCount = p.Aliases.Count() // here of course is the problem
}

显然,当我用 item 的表达式(我有并且知道如何使用)构建它时,我想用“p”替换表达式。

TLDR 是否有一种简单的方法可以将 LambdaExpression 中使用的参数的所有实例替换为另一个表达式?

【问题讨论】:

  • 可能喜欢stackoverflow.com/questions/5430996/…?让我知道是否可以回答(“访客”方法是第一个尝试的方法)
  • 您愿意解释一下吗?我已阅读所有但不明白:/ p => p.Names.Where(n => n.StartsWith("hello")) - 是单个表达式,其中单个表达式的参数中的一个参数是另一个表达...
  • Marc Gravell,你简直就是男人中的神。你回答这个问题的速度比我把它放在我的代码中并测试它还快!
  • 请注意,我投票结束,因为这是完全重复的

标签: .net linq-to-entities expression-trees


【解决方案1】:

一个帮助您入门的示例。

class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<Foo1, int>> expression1 = a => a.Foo2S.Count();

            Expression<Func<IEnumerable<Foo1>, IEnumerable<Foo2>>> expression2 =
                a => a.Select(b => new Foo2 { String1 = "asdf", Foo2Count = 3 });

            MemberAssignment foo2countAssignment = GetExpression(expression2);
            // in this case, it will be a ConstantExpression with a value of 3.
            var expression = foo2countAssignment.Expression as ConstantExpression;

            Expression<Func<IEnumerable<Foo1>, IEnumerable<Foo2>>> expression3 =
                a => a.Select(b => new Foo2 { String1 = "asdf", Foo2Count = b.Foo2S.Count() });

            foo2countAssignment = GetExpression(expression3);
            // in this case, it will be an Expression<Func<Foo1, int>>
            // exactly the same as expression1, except that it has a different parameter.
            var expressionResult = foo2countAssignment.Expression as MethodCallExpression;
            var foo2SPropertyExpression = expressionResult.Arguments[0] as MemberExpression;
            // This is the "b".Foo2SCount()
            var theBparameter = foo2SPropertyExpression.Expression as ParameterExpression;


            // Practical demonstartion.
            var mce = expression1.Body as MethodCallExpression;

            var selectStatement = expression2.Body as MethodCallExpression;
            var selectLambda = selectStatement.Arguments[1] as Expression<Func<Foo1, Foo2>>;
            var bParameter = selectLambda.Parameters[0];

            var me = mce.Arguments[0] as MemberExpression;
            var newExpression = me.Update(bParameter);
            // Then you go up the expression tree using Update to create new expression till first level.
            // Unless you find a way to replace me.
        }

        public static MemberAssignment GetExpression(Expression<Func<IEnumerable<Foo1>, IEnumerable<Foo2>>> expression2)
        {
            // a."Select"
            var selectStatement = expression2.Body as MethodCallExpression;
            // a.Select("b => new Foo2..."
            var selectLambda = selectStatement.Arguments[1] as Expression<Func<Foo1, Foo2>>;
            // a.Select(b => "new Foo2"
            var newFoo2Statement = selectLambda.Body as MemberInitExpression;
            // a.Select(b => new Foo2 {string1 = "asdf", !!Foo2Count = 3!! })
            return newFoo2Statement.Bindings[1] as MemberAssignment;
        }
    }

        public class Foo1
    {
        public IEnumerable<Foo2> Foo2S { get; set; }
    }

    public class Foo2
    {
        public string String1 { get; set; }
        public int Foo2Count { get; set; }
    }

基本上,这个程序会遍历表达式树并布置每个节点。您可以在每个节点上使用“.GetType()”来获取表达式的确切类型并相应地处理它们。 (在本例中是硬编码和已知的)。

最后一个示例演示了如何将 a.Foo2s.Count() 中的“a”替换为“b”,以便将其替换为第二个较长的表达式。

那么你当然需要想办法自动检测并将所有表达式 1 复制回表达式 2。

不是一件容易的事。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多