如何强制LINQ to SQL来评估数据库中的整个查询?

时间:2011-09-09 16:38:03

标签: c# .net sql linq linq-to-sql

我有一个完全可以转换为SQL的查询。由于未知原因,LINQ决定在.NET中执行的最后一个Select()(不在数据库中),这会导致针对数据库运行大量额外的SQL查询(每个项目)。

实际上,我找到了一种强制完全转换为SQL的“奇怪”方式:

我有一个查询(这是一个非常简化的版本,仍然没有按预期工作):

MainCategories.Select(e => new
{
    PlacementId = e.CatalogPlacementId, 
    Translation = Translations.Select(t => new
    {
        Name = t.Name,
        // ...
    }).FirstOrDefault()
})

它将生成大量SQL查询:

SELECT [t0].[CatalogPlacementId] AS [PlacementId]
FROM [dbo].[MainCategories] AS [t0]

SELECT TOP (1) [t0].[Name]
FROM [dbo].[Translations] AS [t0]

SELECT TOP (1) [t0].[Name]
FROM [dbo].[Translations] AS [t0]

...

但是,如果我追加另一个只复制所有成员的Select()

.Select(e => new
{
    PlacementId = e.PlacementId, 
    Translation = new
    {
        Name = e.Translation.Name,
        // ...
    }
})

它将把它编译成一个SQL语句:

SELECT [t0].[CatalogPlacementId] AS [PlacementId], (
    SELECT [t2].[Name]
    FROM (
        SELECT TOP (1) [t1].[Name]
        FROM [dbo].[Translations] AS [t1]
        ) AS [t2]
    ) AS [Name]
FROM [dbo].[MainCategories] AS [t0]

任何线索为什么? 如何强制LINQ to SQL更一般地生成单个查询(没有第二次复制Select())?

注意:我已经更新了查询,以使其变得非常简单。

PS:只是,我的想法是对具有类似模式的查询进行后处理/转换(添加另一个Select())。

3 个答案:

答案 0 :(得分:6)

当您在SingleOrDefault中呼叫MyQuery时,您正在执行查询,并将结果加载到客户端。

SingleOrDefault返回IEnumerable<T>,不再是IQueryable<T>。此时您已经强制它将在客户端上进行所有进一步处理 - 它无法再执行SQL组合。

答案 1 :(得分:1)

不完全确定发生了什么,但我发现你写这个查询的方式非常“奇怪”。我会这样写,并怀疑这会起作用:

        var q = from e in MainCategories
                let t = Translations.Where(t => t.Name == "MainCategory" 
                    && t.RowKey == e.Id 
                    && t.Language.Code == "en-US").SingleOrDefault()
                select new TranslatedEntity<Category>
                           {
                               Entity = e,
                               Translation = new TranslationDef
                                                 {
                                                     Language = t.Language.Code,
                                                     Name = t.Name,
                                                     Xml = t.Xml
                                                 }
                           };

我总是尝试将from部分(数据源的选择)与select部分(投影到目标类型)分开。我发现它也更容易阅读/理解,而且它通常也是与大多数linq提供商合作更好。

答案 2 :(得分:1)

您可以按如下方式编写查询以获得所需的结果:

MainCategories.Select(e => new
{
    PlacementId = e.CatalogPlacementId, 
    TranslationName = Translations.FirstOrDefault().Name,
})

据我所知,这是由于LINQ如何投影查询。我认为当它看到嵌套的Select时,它不会将其投影到多个子查询中,因为本质上就是需要的,因为IIRC你不能在SQL中使用子查询的多个返回列,所以LINQ将其更改为每行查询。带有列访问器的FirstOrDefault似乎是对SQL中发生的事情的直接转换,因此LINQ-SQL知道它可以编写子查询。

第二个Select必须投影查询,类似于我上面的编写方式。没有挖掘反射器就很难确认。通常,如果我需要选择多个列,我会使用如下所示的let语句:

from e in MainCategories
let translation = Translations.FirstOrDefault()
select new
{
    PlacementId = e.CatalogPlacementId, 
    Translation = new {
       translation.Name,
    }
})