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

我有一个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语法.

最佳答案 如评论中所述,您需要
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).

点赞