下面有点长。如果您只想让它工作,而不关心为什么或如何工作,那么请跳到最后两个代码部分。
托马斯·佩特里切克的
answer
TResult
属于
resultSelector
让我们看看我之前的问题:
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"]};
这可以解释为:
var intermediate =
table1.Join(
table2, t1=>t1["ID"], t2=>t2["ID"],
(t1, t2)=>new{t1=t1, t2=t2}
);
TableView result =
intermediate.Join(
table3, anon=>anon.t1["ID"], t3=>t3["ID"],
(anon, t3)=>new[]{anon.t1["ID"], anon.t2["ID"], t3["Foo"]}
);
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"]
join t4 in table4 on t1["ID"] equals t4["ID"]
select new[]{t1["ID"], t2["Description"], t3["Foo"], t4["Bar"]};
var intermediate1 =
table1.Join(
table2, t1=>t1["ID"], t2=>t2["ID"],
(t1, t2)=>new{t1=t1, t2=t2}
);
var intermediate2 =
intermediate1.Join(
table3, anon1=>anon1.t1["ID"], t3=>t3["ID"],
(anon1, t3)=>new{anon1=anon1, t3=t3}
);
TableView result =
intermediate2.Join(
table4, anon2=>anon2.anon1.t1["ID"], t4=>t4["ID"],
(anon2, t3)=>new[]{
anon2.anon1.t1["ID"], anon2.anon1.t2["ID"],
anon2.t3["Foo"], t4["Bar"]
}
);
所以
结果选择器
很明显,我需要的不是一个,而是两个
Join
方法。我在最后一个连接中工作得很好,我需要为中间连接添加另一个连接。记住,我已有的方法返回一个
TableView
:
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));
}
现在我需要添加一个
加入
方法,以便可以在链中调用:
public Table Join<T>(Table inner,
FakeFunc<Table, Projection> otherKeySelector,
FakeFunc<Table, Projection> innerKeySelector,
FakeFunc<Table, Table, T> resultSelector){
Table join = new JoinedTable(this, inner,
new EqualsCondition(outerKeySelector(this), innerKeySelector(inner)));
// calling resultSelector(this, inner) would give me the anonymous type,
// but what would I do with it?
return join;
}
添加此方法使我的连接几乎可以工作。我终于可以连接三个或更多的表,但我丢失了别名:
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"]
// ^ error, 't1' isn't a member of 'Table'
select new[]{t1["ID"], t2["Description"], t3["Foo"]};
// ^ ^ error, 't1' & 't2' aren't members of 'Table'
TableView result =
from t1 in table1
join t2 in table2 on t1["ID"] equals t2["ID"]
join t3 in table3 on table1["ID"] equals t3["ID"]
select new[]{table1["ID"], table2["Description"], t3["Foo"]};
这将编译、运行并产生预期的结果。呜呼!算是成功吧。不过,丢失别名并不理想。在实际查询中,表可能更复杂:
TableView result =
from t1 in table1
join t2 in (
from t in table2
where t["Amount"] > 20
select new[]{t["ID"], t["Description"]
).AsSubQuery() on t1["ID"] equals t2["ID"]
join t3 in table3 on t1["ID"] equals t3["ID"]
select new[]{table1["ID"], t2["Description"], t3["Foo"]};
// ^ error, 't2' isn't a member of 'Table'
在这里,我不能没有这个别名
t2
在多次看到“'t1'不是'Table'的成员”消息后,我终于意识到秘密就在
outerKeySelector
参数到
. LINQ只是在找一个叫做
t1
(或其他)那是这个lambda的论点的一部分。我的
外部选择器
FakeFunc<Table, Projection> outerKeySelector
这个
Table
t1级
dynamic
要做到这一点,但如果我使用的是C#4,那么它的整个设计就会有所不同(我确实计划稍后在C#4中重做这一点,仅针对.NET 4.0客户端,充分利用动态类型提供列投影作为表的实际属性)。不过,在.NET2.0中,我没有动态类型。那么,如何才能创建一个将表别名作为属性的类型呢?
多好的一分钟。别挂电话。这个
已经还给我一个了!不知何故,我需要抓住这个物体并把它传给
在下一个连接中。但是怎么做呢?我不能把它储存在我的房间里
JoinedTable
接合表
描述实际联接的实例,以及对包含别名的匿名类型的引用。尤里卡!
最后,代码的完整功能版本添加了以下类:
class IntermediateJoin<T>{
readonly JoinedTable table;
readonly T aliases;
public IntermediateJoin(JoinedTable table, T aliases){
this.table = table;
this.aliases = aliases;
}
public TableView Join(Table inner,
FakeFunc<T, Projection> outerKeySelector,
FakeFunc<Table, Projection> innerKeySelector,
FakeFunc<T, Table, Projection[]> resultSelector){
var join = new JoinedTable(table, inner,
new EqualsCondition(outerKeySelector(aliases), innerKeySelector(inner)));
return join.Select(resultSelector(aliases, inner));
}
public IntermediateJoin<U> Join<U>(Table inner,
FakeFunc<T, Projection> outerKeySelector,
FakeFunc<Table, Projection> innerKeySelector,
FakeFunc<T, Table, U> resultSelector){
var join = new JoinedTable(table, inner,
new EqualsCondition(outerKeySelector(aliases), innerKeySelector(inner)));
var newAliases = resultSelector(aliases, inner);
return new IntermediateJoin<U>(join, newAliases);
}
}
这种方法
表
public IntermediateJoin<T> Join<T>(Table inner,
FakeFunc<Table, Projection> outerKeySelector,
FakeFunc<Table, Projection> innerKeySelector,
FakeFunc<Table, Table, T> resultSelector){
var join = new JoinedTable(this, inner,
new EqualsCondition(outerKeySelector(this), innerKeySelector(inner)));
var x = resultSelector(this, inner);
return new IntermediateJoin<T>(join, x);
}
这提供了功能齐全的连接语法!
再次感谢托马斯·佩特里切克花时间阅读和理解我的问题,并给我一个深思熟虑的答案。
GroupJoin
和
SelectMany