C# Linq到SQL-二阶投影

C# Linq到SQL-二阶投影,c#,linq,linq-to-sql,C#,Linq,Linq To Sql,考虑以下几点: 我们有一个名为“Items”的表,有10列-a到J,以及一个相关的表Foo 然后我们有以下两个类 假设我有一个方法将数据投影到第一个类中: IQueryable<ItemSmall> Function1() { return Context.Items.Select(i => new ItemSmall { A = i.A, B = i.B, C = i.RelatedTable.FirstOrDefautl(), D = i.RelatedTab

考虑以下几点:

  • 我们有一个名为“Items”的表,有10列-a到J,以及一个相关的表Foo
  • 然后我们有以下两个类

假设我有一个方法将数据投影到第一个类中:

IQueryable<ItemSmall> Function1() {
    return Context.Items.Select(i => new ItemSmall { A = i.A, B = i.B, C = i.RelatedTable.FirstOrDefautl(), D = i.RelatedTable.FirstOrDefautl(), E = i.E });
}
IQueryable函数1(){
返回Context.Items.Select(i=>newitemsall{A=i.A,B=i.B,C=i.RelatedTable.FirstOrDefautl(),D=i.RelatedTable.FirstOrDefautl(),E=i.E});
}
然后,如果我实例化这个查询,它工作得很好——只查询A到E列。我很高兴

现在考虑下一个函数:

IQueryable<ItemSmaller> Function2() {
    return Function1().Select(i => new ItemSmaller { A = i.A, B = i.B });
}
IQueryable函数2(){
return Function1().Select(i=>newitemsaller{A=i.A,B=i.B});
}
这就是魔术似乎失败的地方。仍然查询A列到D列。我想我有点期待L2足够聪明,可以省略C列到J列,因为它们没有投影到最终的对象上

有没有一种方法可以有效地实现像这样的菊花链投影?(我宁愿只编写一次数据库到对象的映射,因为有些投影相当复杂。)

编辑

感谢Sergey验证了上面描述的原始场景完全按照预期工作。我实际场景中的不同之处在于,属性C和D实际上是更复杂的表达式,它们对相关的一对多表利用
FirstOrDefault
操作

似乎
First
/
FirstOrDefault
/
SingleOrDefault
会导致查询它们的依赖列


我已经相应地更新了这个问题。

我的理解是,在第二种情况下,在投影到较小的类之前,您仍然投影到较大的类中,因此需要数据库中的所有字段

实际上,如果要缓存第一个查询的结果,那么可能需要对Function1返回值调用ToList(),并在Function2中使用该列表。这样,数据库只查询一次

希望这有帮助

编辑:在阅读了Sergey的评论之后,我也在LinqPad中进行了测试,还运行了SQL Profiler,似乎我错了,在DB上实际上只查询了较小的字段集。你能解释一下你是如何得出所有字段都被查询的结论吗?

IQueryable Function1(){
IQueryable<ItemSmall> Function1() {
    return Context.Items.Select(i => new ItemSmall { A = i.A, B = i.B, C =     i.RelatedTable.FirstOrDefautl(), D = i.RelatedTable.FirstOrDefautl(), E = i.E });
}
返回Context.Items.Select(i=>newitemsall{A=i.A,B=i.B,C=i.RelatedTable.FirstOrDefautl(),D=i.RelatedTable.FirstOrDefautl(),E=i.E}); }
通过调用i.RelatedTable.FirstOrDefault()或类似函数;您已经明确指示框架查询RelatedTable的第一行(如果有的话),因为它们急于加载


在您的情况下,您需要一个懒惰的want,尝试使用Take(1),但这意味着您需要重新建模您的类,它看起来有点粗糙。

根据
FirstOrDefault()
操作符会立即执行,也就是说,您在编写
FirstOrDefault()
时强制执行数据库查询。这就是为什么你会看到所有栏目都被下载。进一步的查询会出现在内存中。

刚刚在LinqPad中验证过-工作正常,只有来自上一次投影的列是queryedhmm,也许我的真实场景中还发生了其他一些事情。好的,在我的真实场景中,第一个查询是在mix中使用FirstOrDefault()。我本应该测试我发布的更简单的场景,但我没有这么做,因为额外的列碰巧都使用FirstOrDefault表达式。显然,在这一点上,这是一个相当小的“问题”,很容易解决。在大多数情况下,魔法是有效的!;)嗯,我不相信linq to objects实现在任何方面都适用于linq to sql(如果调用Function1()确实是“立即”的,那么调用Function1()将启动db查询,但它只在迭代时启动),但我不希望在确定根本原因后得到任何答案,所以我放弃了检查;)
IQueryable<ItemSmall> Function1() {
    return Context.Items.Select(i => new ItemSmall { A = i.A, B = i.B, C =     i.RelatedTable.FirstOrDefautl(), D = i.RelatedTable.FirstOrDefautl(), E = i.E });
}