2011-04-29 122 views
1

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

例如,说他们有一列,他们可以指定的方式来拉动值从数据库是:

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是p.Aliases.Count()使。

我想接下来的事情是:

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 
} 

显然,我想用“P”来代替表达时,我建立这个与项目表达(我却并知道如何使用) 。

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

+1

像http://stackoverflow.com/questions/5430996/replacing-the-parameter-name-in-the-body-of-an-expression/5431309#5431309也许?让我知道,如果这回答它(“访问者”的方法是第一个尝试) – 2011-04-29 19:31:08

+0

你愿意解释它好一点吗?我读了所有,但不明白:/ p => p.Names.Where(n => n.StartsWith(“hello”)) - 是单个表达式,其中单个表达式的参数中的一个参数是另一个表达式表达... – 2011-04-29 19:34:06

+0

Marc Gravell,你简直就是我的男人之神。你回答这个问题比我能把它放在我的代码中并测试它! – tster 2011-04-29 19:40:44

回答

0

一个让你开始的例子。

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”,以便它可以替换为第二个更长的表达式。

然后ofcourse你需要想办法让你可以自动检测和复制的所有表达1 backinto表达2.

不是一件容易的事。

+0

请注意,这个SO问题http://stackoverflow.com/questions/5430996/replacing-the-parameter-name-in-the-body-of-an-表达/ 5431309#5431309直接回答我的问题。 – tster 2011-05-12 13:08:32