2011-04-29 14 views
1

私は、LINQ to Entitiesに送信される式ツリーを動的に生成しています。私はフレームワークを提供しており、開発者はラムダ式として出力列を指定できます。例えば2つのLambdaExpressionsを1つのLamdaExpressionに結合する

、彼らは、データベースから値をプルする方法があるように指定することができ、列を持っていると言う:

p => p.Aliases.Count() 

と別の列は次のとおりです。

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

問題はここにあります私はこれらの両方を単一の式に組み合わせる必要があることを示します。

getter = field.SelectorExpression.Body; 

そして私は、LINQは、Pがp.Aliases.Count()であるかを知ることができないのでので、作るのエラーメッセージThe parameter 'p' was not bound in the specified LINQ to Entities query expressionを取得:

私の最初の試みでした。

私が試した次の事した

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

はしかし、その後、私はまた、私はSQL Serverは、ラムダ式を実行する方法を知っていることを期待していないのでので、作るのメッセージThe LINQ expression node type 'Invoke' is not supported in LINQ to Entities.を受け取ります。

今のところ基本的にこの表現を持っている:

明らか
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で使用されるパラメータのすべてのインスタンスを別のExpressionに簡単に置き換えることはできますか?

+1

ようにhttp://stackoverflow.com/questions/5430996/replacing-the-parameter-name-in-the-body-of-an-expression/5431309#5431309多分?それが答えるかどうかを教えてください。(「訪問者」のアプローチが最初に試しています) –

+0

もう少し説明してもらえますか? p => p.Names.Where(n => n.StartsWith( "hello")) - 単一の式に対するパラメータのうちの1つが別の式である単一の式です表現... –

+0

マーク・グラヴェル、あなたは文字通り私は男性の間で神です。あなたはこれを私のコードに入れてテストするよりも速く答えました! – tster

答えて

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"に置き換えて、より長い2番目の式に置き換える方法を示しています。あなたが自動検出し、式1 backinto式のすべて2.

ない簡単なタスクを複製しことができるような方法を考える必要がある勿論その後

+0

この問題はhttp://stackoverflow.com/questions/5430996/replacing-the-parameter-name-in-the-body-of-an-式/ 5431309#5431309私の質問に直接答えました。 – tster

関連する問題