title: Grafting LINQ onto C# 2 library
body: <p>I'm writing a data access layer.  It will have C# 2 and C# 3 clients, so I'm compiling against the 2.0 framework.  Although encouraging the use of stored procedures, I'm still trying to provide a fairly complete ability to perform ad-hoc queries.  I have this working fairly well, already.</p>
  
  <p>For the convenience of C# 3 clients, I'm trying to provide as much compatibility with LINQ query syntax as I can.  Jon Skeet <a href="http://msmvps.com/blogs/jon_skeet/archive/2008/02/29/odd-query-expressions.aspx" rel="nofollow noreferrer" title="Odd query expressions">noticed</a> that LINQ query expressions are duck typed, so I don't <em>have</em> to have an <code>IQueryable</code> and <code>IQueryProvider</code> (or <code>IEnumerable&lt;T&gt;</code>) to use them.  I just have to provide methods with the correct signatures.</p>
  
  <p>So I got <code>Select</code>, <code>Where</code>, <code>OrderBy</code>, <code>OrderByDescending</code>, <code>ThenBy</code>, and <code>ThenByDescending</code> working.  Where I need help are with <code>Join</code> and <code>GroupJoin</code>.  I've got them working, but only for one join.</p>
  
  <p>A brief compilable example of what I have is this:</p>
  
  <pre><code>// .NET 2.0 doesn't define the Func&lt;...&gt; delegates, so let's define some workalikes
  delegate TResult FakeFunc&lt;T, TResult&gt;(T arg);
  delegate TResult FakeFunc&lt;T1, T2, TResult&gt;(T1 arg1, T2 arg2);
  
  abstract class Projection{
      public static Condition operator==(Projection a, Projection b){
          return new EqualsCondition(a, b);
      }
      public static Condition operator!=(Projection a, Projection b){
          throw new NotImplementedException();
      }
  }
  class ColumnProjection : Projection{
      readonly Table  table;
      readonly string columnName;
  
      public ColumnProjection(Table table, string columnName){
          this.table      = table;
          this.columnName = columnName;
      }
  }
  abstract class Condition{}
  class EqualsCondition : Condition{
      readonly Projection a;
      readonly Projection b;
  
      public EqualsCondition(Projection a, Projection b){
          this.a = a;
          this.b = b;
      }
  }
  class TableView{
      readonly Table        table;
      readonly Projection[] projections;
  
      public TableView(Table table, Projection[] projections){
          this.table       = table;
          this.projections = projections;
      }
  }
  class Table{
      public Projection this[string columnName]{
          get{return new ColumnProjection(this, columnName);}
      }
  
      public TableView Select(params Projection[] projections){
          return new TableView(this, projections);
      }
      public TableView Select(FakeFunc&lt;Table, Projection[]&gt; projections){
          return new TableView(this, projections(this));
      }
      public Table     Join(Table other, Condition condition){
          return new JoinedTable(this, other, condition);
      }
      public TableView Join(Table inner,
                            FakeFunc&lt;Table, Projection&gt; outerKeySelector,
                            FakeFunc&lt;Table, Projection&gt; innerKeySelector,
                            FakeFunc&lt;Table, Table, Projection[]&gt; resultSelector){
          Table join = new JoinedTable(this, inner,
              new EqualsCondition(outerKeySelector(this), innerKeySelector(inner)));
          return join.Select(resultSelector(this, inner));
      }
  }
  class JoinedTable : Table{
      readonly Table     left;
      readonly Table     right;
      readonly Condition condition;
  
      public JoinedTable(Table left, Table right, Condition condition){
          this.left      = left;
          this.right     = right;
          this.condition = condition;
      }
  }
  </code></pre>
  
  <p>This allows me to use a fairly decent syntax in C# 2:</p>
  
  <pre><code>Table table1 = new Table();
  Table table2 = new Table();
  
  TableView result =
      table1
      .Join(table2, table1["ID"] == table2["ID"])
      .Select(table1["ID"], table2["Description"]);
  </code></pre>
  
  <p>But an even nicer syntax in C# 3:</p>
  
  <pre><code>TableView result =
      from t1 in table1
      join t2 in table2 on t1["ID"] equals t2["ID"]
      select new[]{t1["ID"], t2["Description"]};
  </code></pre>
  
  <p>This works well and gives me identical results to the first case.  The problem is if I want to join in a third table.</p>
  
  <pre><code>TableView result =
      from t1 in table1
      join t2 in table2 on t1["ID"] equals t2["ID"]
      join t3 in table3 on t1["ID"] equals t3["ID"]
      select new[]{t1["ID"], t2["Description"], t3["Foo"]};
  </code></pre>
  
  <p>Now I get an error (Cannot implicitly convert type 'AnonymousType#1' to 'Projection[]'), presumably because the second join is trying to join the third table to an anonymous type containing the first two tables.  This anonymous type, of course, doesn't have a <code>Join</code> method.</p>
  
  <p>Any hints on how I can do this?</p>