我有一个完全可以转换为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()
)。
答案 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,
}
})