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

发布于 2024-12-03 19:39:01 字数 1418 浏览 0 评论 0原文

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

I have a query which is fully translatable to SQL. For unknown reasons LINQ decides the last Select() to execute in .NET (not in the database), which causes to run a lot of additional SQL queries (per each item) against database.

Actually, I found a 'strange' way to force the full translation to SQL:

I have a query (this is a really simplified version, which still does not work as expected):

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

It will generates a lot of SQL queries:

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]

...

However, if I append another Select() which just copies all members:

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

It will compile it into a single SQL statement:

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]

Any clues why? How to force the LINQ to SQL to generate a single query more generically (without the second copying Select())?

NOTE: I've updated to query to make it really simple.

PS: Only, idea I get is to post-process/transform queries with similar patterns (to add the another Select()).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

青瓷清茶倾城歌 2024-12-10 19:39:01

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

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

When you call SingleOrDefault in MyQuery, you are executing the query at that point which is loading the results into the client.

SingleOrDefault returns IEnumerable<T> which is no longer an IQueryable<T>. You have coerced it at this point which will do all further processing on the client - it can no longer perform SQL composition.

乖乖哒 2024-12-10 19:39:01

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

        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 提供程序。

Not entirely sure what is going on, but I find the way you wrote this query pretty 'strange'. I would write it like this, and suspect this will work:

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

I always try to separate the from part (selection of the datasources) from the select part (projection to your target type. I find it also easier to read/understand, and it generally also works better with most linq providers.

明月松间行 2024-12-10 19:39:01

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

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

You can write the query as follows to get the desired result:

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

As far as i'm aware, it's due to how LINQ projects the query. I think when it see's the nested Select, it will not project that into multiple sub-queries, as essentially that would be what would be needed, as IIRC you cannot use multiple return columns from a sub-query in SQL, so LINQ changes this to a query-per-row. FirstOrDefault with a column accessor seems to be a direct translation to what would happen in SQL and therefore LINQ-SQL knows it can write a sub-query.

The second Select must project the query similar to how I have written it above. It would be hard to confirm without digging into a reflector. Generally, if I need to select many columns, I would use a let statement like below:

from e in MainCategories
let translation = Translations.FirstOrDefault()
select new
{
    PlacementId = e.CatalogPlacementId, 
    Translation = new {
       translation.Name,
    }
})
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文