2016-04-04 2 views
2

次のデモクラスを想定しましょう。Lambda in Linq AST - なぜ異なる動作ですか?

public class Foo { 
    public int key1 {get; set;} 
    public Foo(int _key1) { 
     key1 = _key1; 
    } 
} 
public class Bar { 
    public int key2 {get; set;} 
    public Bar(int _key2) { 
     key2 = _key2; 
    } 
} 

これらは単純なLinq結合で結合されています。

Foo[]aSet = new Foo[3]{new Foo(1),new Foo(2),new Foo(3)}; 
Bar[]bSet = new Bar[3]{new Bar(1),new Bar(3),new Bar(5)}; 

Func<int,Func<Foo,bool>> VisibleLambda = w => x => x.key1 > w; 
var pb = Expression.Parameter(typeof(Bar),"z"); 
var pf = Expression.Parameter(typeof(Foo), "y"); 
PropertyInfo BarId = typeof(Bar).GetProperty("key2"); 
PropertyInfo FooId = typeof(Foo).GetProperty("key1"); 
var eqexpr = Expression.Equal(Expression.Property(pb, BarId), Expression.Property(pf, FooId)); 
var lambdaInt = Expression.Lambda<Func<Bar, bool>>(eqexpr, pb); 
var InvisibleLambda = Expression.Lambda<Func<Foo,Func<Bar, bool>>>(lambdaInt,pf); 

var query = from a in aSet.Where(VisibleLambda(1)) 
    from b in bSet.Where(InvisibleLambda.Compile()(a)) 
    select new Tuple<Foo,Bar>(a,b); 

さて、クエリは、実装の詳細は無関係である拡張

IQueryable<TElement> IQueryProvider.CreateQuery<TElement>(Expression expression) 
{ 
    if (expression == null) 
     throw new ArgumentNullException("expression"); 

    return new ExpressionQueryImpl<TElement>(DataContextInfo, expression); 
} 

を介して実装されています。私の質問は、唯一のIQueryable由来の発現に関連しています。 2つのラムダがあります:NodeType Quoteの式の引数として1つ( "visible")が生成され、解析が非常に簡単ですが、もう1つ( "不可視")は式の第2引数として生成されますNodeType Invokeのwhere句は、そのSQLレンダリングの観点からほとんど見えません。 なぜそれが起こっていて、回避策を講じてツアーをするのですか?

+1

あなたは 'Expression'sについて話していますが、' Func'sでサンプルを表示しています。ケースを提示する関連情報のみを残して、投稿を更新してください。 –

+0

@IvanStoev「目に見えない」LambdaExpressionについては、「問題」の原因であるため、より適切なLambdaExpressionのために完了しました –

+1

今、私はあなたが何を意味するのか見ていますが、奇妙なので、私はクエリプロバイダがそれらを使用することはできません驚くことではない。たとえば、 'Queryable.Where'は' Expression > 'を期待しています。すなわち' T'パラメータをとり、 'Fool'ではなく' bool'を返すラムダです。 –

答えて

2

としてはQueryable.Where

Hereから予想異なる署名によるもので、コメント1イヴァンStoev、異なる行動の2特にSQL生成の問題が指摘さイゴールからの溶液でありますTkachev、興味のある人なら誰でも。

すべては1つが適切な署名LINQメソッドを活用できる有用な拡張を実装するに沸く:

static class ExpressionTestExtensions 
{ 
    public class LeftJoinInfo<TOuter,TInner> 
    { 
     public TOuter Outer; 
     public TInner Inner; 
    } 

    [ExpressionMethod("LeftJoinImpl")] 
    public static IQueryable<LeftJoinInfo<TOuter,TInner>> LeftJoin<TOuter, TInner, TKey>(
     this IQueryable<TOuter> outer, 
     IEnumerable<TInner> inner, 
     Expression<Func<TOuter, TKey>> outerKeySelector, 
     Expression<Func<TInner, TKey>> innerKeySelector) 
    { 
     return outer 
      .GroupJoin(inner, outerKeySelector, innerKeySelector, (o, gr) => new { o, gr }) 
      .SelectMany(t => t.gr.DefaultIfEmpty(), (o,i) => new LeftJoinInfo<TOuter,TInner> { Outer = o.o, Inner = i }); 
    } 

    static Expression<Func< 
     IQueryable<TOuter>, 
     IEnumerable<TInner>, 
     Expression<Func<TOuter,TKey>>, 
     Expression<Func<TInner,TKey>>, 
     IQueryable<LeftJoinInfo<TOuter,TInner>>>> 
     LeftJoinImpl<TOuter, TInner, TKey>() 
    { 
     return (outer,inner,outerKeySelector,innerKeySelector) => outer 
      .GroupJoin(inner, outerKeySelector, innerKeySelector, (o, gr) => new { o, gr }) 
      .SelectMany(t => t.gr.DefaultIfEmpty(), (o,i) => new LeftJoinInfo<TOuter,TInner> { Outer = o.o, Inner = i }); 
    } 

} 

:-) Queryable.GroupJoinは、このような拡張機能を定義した。すなわち、私の " 「最後に、エレガントなユースケースは、単に

なり

 static internal IQueryable<ExpressionTestExtensions.LeftJoinInfo<T2,T1>> NewJoin<T1, T2, TKey>(Expression<Func<T2, TKey>> outer, Expression<Func<T1, TKey>> inner) 
     where T2: class 
     where T1 : class 
    { 


     using (var db = new MyContext()) { 

     var query = (from b in db.GetTable<T2>() select b).LeftJoin <T2,T1, TKey>((from f in db.GetTable<T1>() select f), outer, inner); 


      return query; 

     } 
    } 
} 

になります一般的な参加

 public static void Main(string[] args) 
    { 
     Console.WriteLine("Hello World!"); 

     //var queryList = Test.Join<Bar, Foo>(b => q => q.id == b.id); 


     var queryList = Test.NewJoin<Bar, Foo, int>(q => q.id, b => b.id); 

     foreach (var telement in queryList) 
     { 
      var bar = telement.Inner as Bar; 
      var element = telement.Outer as Foo; 
      Console.WriteLine(element.id.ToString() + " " + element.FromDate.ToShortDateString() +" " 
           +bar.id.ToString() + " " + bar.Name 
          ); 
     } 

     Console.Write("Press any key to continue . . . "); 
     Console.ReadKey(true); 
    } 
関連する問題