【发布时间】:2011-02-05 01:56:15
【问题描述】:
我正在编写一个数据访问层。它将具有 C# 2 和 C# 3 客户端,因此我正在针对 2.0 框架进行编译。尽管鼓励使用存储过程,但我仍在尝试提供相当完整的能力来执行即席查询。我已经把它做得很好了。
为了方便 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”隐式转换为“Projection[]”),可能是因为第二个连接试图将第三个表连接到包含前两个表的匿名类型。当然,这种匿名类型没有Join 方法。
关于如何做到这一点的任何提示?
【问题讨论】:
标签: c# linq data-access-layer