使用导航属性的LINQ查询生成多个SELECT语句

时间:2013-08-24 03:30:51

标签: c# sql linq entity-framework linq-to-entities

我有一个POCO Domain Entity类,其中包含导航到相关记录的便捷方法。我正在使用AdventureWorks2008R2数据库来演示我正在尝试完成的任务。所有这些查询都可以在LINQPad中运行,以观察生成的SQL语句。

SalesOrderHeaders.Where(s => s.SalesOrderID == 43659)
                 .Single().SalesOrderDetails

此语句生成2个SQL语句。一个用于SalesOrderHeader记录,另一个用于检索SalesOrderDetails。现在考虑这个导航到另一个相关表的语句:

SalesOrderHeaders.Where(s => s.SalesOrderID == 43659)
                 .Single().SalesOrderDetails.Select(s => s.SpecialOfferProduct)

检索单个SalesOrderHeader记录后,域类将包含如下的便利属性:

public IQueryable<SpecialOfferProduct> SpecialProducts
    {
        get
        {
            return SalesOrderDetails.Where(sod => sod.OrderQty > 3)
                                    .Select(s => s.SpecialOfferProduct)
                                    .AsQueryable();
        }
    }

该语句产生多个SELECT语句:一个用于SpecialOfferProduct中的每个记录。我的问题是:为什么导航属性不会产生单个SELECT语句?这是一个巨大的性能问题,因为它会产生很多不必要的喋喋不休。我可以使用LINQ SQL语法,但这只是在使用存储库创建原始查询时。在这种情况下,我有一个SalesOrderHeader对象的实例,并且无法访问类中的Context或Repository。有没有办法强制它使用JOIN创建一个SELECT语句?

如果没有办法,我想在我的存储库中创建一个额外的方法来填充这些属性。问题是我有两个步骤:1检索SalesOrderHeader对象,然后使用适当的LINQ语句填充其他属性,这将迫使JOIN语法。

1 个答案:

答案 0 :(得分:2)

如评论中所述,您需要Include方法:

SalesOrderHeaders.Include(s => s.SalesOrderDetails
                                .Select(d => d.SpecialOfferProduct))
                 .Where(s => s.SalesOrderID == 43659)
                 .Single().SalesOrderDetails

这将加入所需的数据(在SQL中)并填充导航属性。

但请注意,您不能使用

之类的语法
.Include(s => s.SalesOrderDetails.Where(sod => sod.OrderQty > 3)
               .Select(d => d.SpecialOfferProduct))

这似乎会部分填充SalesOrderDetails。有一些变更请求要求EF团队实施,但到目前为止,还没有完成。

另一方面注意,将SpecialProducts作为IQueryable返回是没用的,因为对集合的后续查询无论如何都不会转换为SQL。您只能在内存语句中访问该属性,而不是在linq-to-enitites查询中(EF无法将该属性转换为SQL)。