我在寫數據訪問層。它將擁有C#2和C#3客戶端,所以我正在編譯2.0框架。儘管鼓勵使用存儲過程,但我仍然試圖提供一種相當完備的功能來執行臨時查詢。我已經很好地工作了。將LINQ嫁接到C#2庫
爲了方便C#3客戶端,我試圖儘可能多地提供與LINQ查詢語法的兼容性。 Jon Skeet noticed LINQ查詢表達式是鴨子鍵入的,所以我不要有有一個IQueryable
和IQueryProvider
(或IEnumerable<T>
)使用它們。我只需提供具有正確簽名的方法。
所以我就Select
,Where
,OrderBy
,OrderByDescending
,ThenBy
和ThenByDescending
工作。我需要幫助的地方是Join
和GroupJoin
。我有他們的工作,但只有一個加入。
什麼,我有一個簡短的編譯的例子是這樣的:
// .NET 2.0 doesn't define the Func<...> delegates, so let's define some workalikes
delegate TResult FakeFunc<T, TResult>(T arg);
delegate TResult FakeFunc<T1, T2, TResult>(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<Table, Projection[]> 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<Table, Projection> outerKeySelector,
FakeFunc<Table, Projection> innerKeySelector,
FakeFunc<Table, Table, Projection[]> 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;
}
}
這讓我在C#2使用一個相當不錯的語法:
Table table1 = new Table();
Table table2 = new Table();
TableView result =
table1
.Join(table2, table1["ID"] == table2["ID"])
.Select(table1["ID"], table2["Description"]);
但在C#3的甚至更好的語法:
TableView result =
from t1 in table1
join t2 in table2 on t1["ID"] equals t2["ID"]
select new[]{t1["ID"], t2["Description"]};
這個效果很好,給了我第一個案例相同的結果。問題是如果我想加入第三個表格。
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"]};
現在,我得到一個錯誤(不能鍵入「AnonymousType#1」隱式轉換爲「投影[]」),大概是因爲第二個連接正在嘗試加入第三個表包含前兩個匿名類型表。這種匿名類型當然沒有Join
方法。
任何提示我如何做到這一點?
謝謝你,你指出我的正確道路。考慮到你的建議,我開始用IEnumerables嘲笑事物,並在Reflector中四處探索。經過足夠的戳動,我想出了我需要做的事情。好消息:不需要運行時反射。不過,它需要額外的課程。我會在另一個答案中發佈完整的細節。不過,你會得到接受的答案和+1。 – 2010-04-07 06:28:30
@P爸爸:我很高興我的答案有幫助 - 我沒有意識到你可以通過添加一個需要數組的重載來從匿名類型中恢復 - 這是一個很好的竅門! – 2010-04-07 13:24:17