Как я могу уменьшить количество дБ в этом Linq2Sql? - PullRequest
5 голосов
/ 16 декабря 2008

У меня есть следующий Linq2Sql, и он выполняет более одного обхода для моего оператора SELECT. Я не уверен почему. Сначала код, затем объяснение: -

from p in db.Questions
select new Models.Question
{
    Title = p.Title,
    TagList = (from t in p.QuestionTags
               select t.Tag.Name).ToList()
}

Теперь база данных

Вопросы <-одно ко многим-> Вопросы-теги <-мани к одному-> Метка

поэтому один вопрос имеет один ко многим тегам с таблицей ссылок в середине. Таким образом, я могу повторно использовать теги несколько раз. (Я открыт для лучшей схемы, если она есть).

При этом выполняется следующий код Sql, сгенерированный Linq2Sql

SELECT [t0].[QuestionId] AS [ID], etc....  <-- that's the good one

.

exec sp_executesql N'SELECT [t1].[Name]
FROM [dbo].[QuestionTags] AS [t0]
INNER JOIN [dbo].[Tags] AS [t1] ON [t1].[TagId] = [t0].[TagId]
WHERE [t0].[QuestionId] = @x1',N'@x1 int',@x1=1

Второй блок SQL указан в списке 2x .. Я думаю, это потому, что первый блок SQL возвращает два результата, поэтому второй запускается для каждого результата из первого.

Можно ли как-нибудь сделать этот оператор SQL вместо 1 + n, где n = количество результатов первого запроса?

Обновление:

Я пробовал загружать Eager и Lazy, и нет никакой разницы.

DataLoadOptions dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Question>(x => x.QuestionTags);
dataLoadOptions.LoadWith<QuestionTag>(x => x.Tag);
db.LoadOptions = dataLoadOptions;

Ответы [ 5 ]

5 голосов
/ 16 декабря 2008

ToList () определенно сдерживает вас. Вы должны выполнить ToList () для всего запроса.

Еще одна вещь, которую я думаю, что вы можете сделать, это использовать "let". Я думаю, что в этом случае он может создать отложенное выполнение и быть включен в дерево выражений, но YMMV.

from p in db.Questions
let Tags = (from t in p.QuestionTags
               select t.Tag.Name)
select new Models.Question
{
    Title = p.Title,
    TagList = Tags
}
2 голосов
/ 16 декабря 2008

Вы можете попробовать настроить Eager Loading для этой ассоциации объектов. Что-то вроде:

        var dlo = new DataLoadOptions();
        // Configure eager loading
        dlo.LoadWith<Question>(q => q.QuestionTags);

        _context = new WhateverContext();
        _context.LoadOptions = dlo;

Но вам, возможно, придется немного изменить свой код. По сути, вы говорите фреймворку, чтобы он выдавал SQL, чтобы вытянуть более широкий граф объектов, а не ждать, пока не будет получен доступ к ассоциации объектов (ленивая загрузка по умолчанию).

Может быть, увидеть (http://blog.codeville.net/2007/12/02/linq-to-sql-lazy-and-eager-loading-hiccups/). Кстати, Стивен другой!

2 голосов
/ 16 декабря 2008

Это может быть одним из случаев, когда само по себе LINQ недостаточно. Рассматривали ли вы написание этой логики вместо UDF или SPROC и просто использовали LINQ для ее вызова? LINQ-to-SQL также очень хорош в вызовах (Entity Framework не так хорош в UDF).

Затем вы можете объединить тег в базе данных и вернуть его, например, как varchar. Для этого без курсора есть трюк TSQL:

DECLARE @foo varchar(max)
SET @foo = ''
SELECT @foo = @foo + [SomeColumn] + ',' -- CSV
FROM [SomeTable]
WHERE -- some condition

(возможно, удаление запятой)

После запуска @foo будет CSV значений - очень эффективно, если вы возвращаете одну строку. Не так здорово, если вы возвращаете несколько основных строк.

0 голосов
/ 01 февраля 2010

Вы можете выполнить ленивую загрузку так:

from p in db.Questions
let Tags = GetTags(Questions.Id)
select new Models.Question
{
    Title = p.Title,
    TagList = LazyList<string>(Tags)
}

public IQueryable<string> GetTags(int questionId) {
    from qt in db.QuestionTags
    join t in db.Tags on qt.TagId equals t.Id
    where qt.questionId = questionId
    select t.Name
}

LazyList - это контейнер IQueryable, который реализует IList. Как только свойство TagList будет перечислено, будет выполнен хранимый внутри IQueryable.

Класс LazyList был написан Робом Коннери и может быть найден здесь: http://blog.wekeroad.com/blog/lazy-loading-with-the-lazylist/

0 голосов
/ 16 декабря 2008

Полагаю, проблема в том, что вам следует также вызывать .ToList () для всего запроса. Это вернет всю коллекцию по одному из БД.

В вашем случае первая команда SQL возвращает только идентификаторы всех вопросов, а затем для каждого вопроса происходит один вызов SQL (во время итерации в цикле foreach) - см. Параметр @ x1.

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