Linq to SQL - 二阶预测

时间:2014-01-03 01:08:21

标签: c# linq linq-to-sql

请考虑以下事项:

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

public class ItemSmall
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
    public string D { get; set; }
    public string E { get; set; }
}

public class ItemSmaller
{
    public string A { get; set; }
    public string B { get; set; }
}

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

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 });
}

如果我然后实例化这个查询它可以很好地工作 - 只查询A到E列。我很高兴。

现在考虑下一个功能:

IQueryable<ItemSmaller> Function2() {
    return Function1().Select(i => new ItemSmaller { A = i.A, B = i.B });
}

这就是魔术似乎失败的地方。仍然会查询A到D列。我想我有点期望L2S足够聪明,可以省略C到J的列,因为它们没有被投射到最终对象上。

是否有任何方法可以以有效的方式进行菊花链式投影? (我更愿意只编写一次数据库到对象的映射,因为一些预测非常复杂。)

修改

感谢谢尔盖验证上述原始场景完全符合要求。我的实际场景的不同之处在于属性C和D实际上是更复杂的表达式,它对相关的一对多表利用FirstOrDefault操作。

似乎First / FirstOrDefault / SingleOrDefault会导致他们的依赖列被无关紧要地查询。

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

3 个答案:

答案 0 :(得分:1)

根据Classification of Standard Query Operators by Manner of Execution FirstOrDefault()运算符立即执行,即您在编写FirstOrDefault()时强制执行数据库查询。这就是您看到所有列都被下载的原因。进一步的查询发生在内存中。

答案 1 :(得分:0)

我的理解是,在第二种情况下,你仍然会投射到较大的类中,然后投射到较小的类中,因此需要来自DB的所有字段。

实际上,如果你要做的是缓存第一个查询的结果,那么你可能想要在Function1返回值上调用ToList(),并在Function2中使用该List。这样,DB只会被查询一次。

希望这有帮助。

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

答案 2 :(得分:0)

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 });
}

通过调用i.RelatedTable.FirstOrDefault()或者喜欢;您已明确指示框架查询RelatedTable的第一行(如果有),因为它们是急切加载的。

在你的情况下,你会想要一个懒惰的想法,尝试使用Take(1),但这意味着你要重新模拟你的类,它看起来有点hacky。