Как заставить LINQ to SQL оценить весь запрос в базе данных? - PullRequest
14 голосов
/ 09 сентября 2011

У меня есть запрос, который полностью переводим в 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()).

Ответы [ 3 ]

6 голосов
/ 09 сентября 2011

Когда вы вызываете SingleOrDefault в MyQuery, вы выполняете запрос в тот момент, который загружает результаты в клиент.

SingleOrDefault возвращает IEnumerable<T>, который больше не является IQueryable<T>. Вы принудили его в этот момент, который будет выполнять всю дальнейшую обработку на клиенте - он больше не может выполнять композицию SQL.

1 голос
/ 13 сентября 2011

Вы можете написать запрос следующим образом, чтобы получить желаемый результат:

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,
    }
})
1 голос
/ 10 сентября 2011

Не совсем уверен, что происходит, но я нахожу способ, которым вы написали этот запрос, довольно "странным". Я бы написал это так и подозреваю, что это сработает:

        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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...