Как загрузить многие ко многим запросам LINQ? - PullRequest
6 голосов
/ 17 сентября 2008

У меня следующая (довольно стандартная) структура таблицы:

Post <-> PostTag <-> Tag

Предположим, у меня есть следующие записи:

PostID Title
1,     'Foo'
2,     'Bar'
3,     'Baz'

TagID Name
1,    'Foo'
2,    'Bar'

PostID TagID
1      1
1      2
2      2

Другими словами, в первом сообщении есть два тега, во втором - один, а в третьем нет.

Я хотел бы загрузить все сообщения и их теги в одном запросе , но не смог найти правильную комбинацию операторов. Я смог загрузить либо постов только с тегами , либо повторных постов, когда более одного тега .

Учитывая приведенную выше базу данных, Я бы хотел получить три сообщения и их теги (если есть) в свойстве коллекции объектов Post . Возможно ли это вообще?

Спасибо

Ответы [ 6 ]

2 голосов
/ 17 сентября 2008

Ура! Это сработало.

Если у кого-то возникла такая же проблема, вот что я сделал:

public IList<Post> GetPosts(int page, int record)
{
    var options = new DataLoadOptions();
    options.LoadWith<Post>(p => p.PostTags);
    options.LoadWith<PostTag>(pt => pt.Tag);
    using (var db = new DatabaseDataContext(m_connectionString))
    {
        var publishDateGmt = (from p in db.Posts
                              where p.Status != PostStatus.Hidden
                              orderby p.PublishDateGmt descending
                              select p.PublishDateGmt)
                              .Skip(page * record)
                              .Take(record)
                              .ToList()
                              .Last();
        db.LoadOptions = options;
        return (from p in db.Posts
                where p.Status != PostStatus.Closed 
                    && p.PublishDateGmt >= publishDateGmt
                orderby p.PublishDateGmt descending
                select p)
                .Skip(page * record)
                .ToList();
    }
}

Выполняет только два запроса и загружает все теги для каждого сообщения.

Идея состоит в том, чтобы получить какое-то значение для ограничения запроса в последнем необходимом сообщении (в этом случае будет достаточно столбца PublishDateGmt), а затем ограничить второй запрос этим значением вместо Take ().

Спасибо за вашу помощь, сирокко.

1 голос
/ 17 сентября 2008

Извините. Решение, которое вы даете, работает, но я обнаружил, что оно ломается при разбиении на страницы с Take (N). Полный метод, который я использую, следующий:

public IList<Post> GetPosts(int page, int records)
{
    var options = new DataLoadOptions();
    options.LoadWith<Post>(p => p.PostTags);
    options.LoadWith<PostTag>(pt => pt.Tag);
    using (var db = new BlogDataContext())
    {
        db.LoadOptions = options;
        return (from p in db.Posts
                where p.Status != PostStatus.Closed
                orderby p.PublishDateGmt descending
                select p)
                .Skip(page * records)
                //.Take(records)
                .ToList();
    }
}

С прокомментированным методом Take () он генерирует запрос, аналогичный тому, который вы опубликовали, но если я снова добавлю Take (), он генерирует 1 + N x M запросов.

Итак, я предполагаю, что мой вопрос сейчас таков: Есть ли замена метода Take () для разбивки на записи?

Спасибо

1 голос
/ 17 сентября 2008

Это немного странно, потому что

DataLoadOptions o = new DataLoadOptions ( );
o.LoadWith<Listing> ( l => l.ListingStaffs );
o.LoadWith<ListingStaff> ( ls => ls.MerchantStaff );
ctx.LoadOptions = o;

IQueryable<Listing> listings = (from a in ctx.Listings
            where a.IsActive == false 
                            select a);
List<Listing> list = listings.ToList ( );

приводит к запросу типа:

SELECT [t0].*, [t1].*, [t2].*, (
SELECT COUNT(*)
FROM [dbo].[LStaff] AS [t3]
INNER JOIN [dbo].[MStaff] AS [t4] ON [t4].[MStaffId] = [t3].[MStaffId]
WHERE [t3].[ListingId] = [t0].[ListingId]
) AS [value]
FROM [dbo].[Listing] AS [t0]
LEFT OUTER JOIN ([dbo].[LStaff] AS [t1]
INNER JOIN [dbo].[MStaff] AS [t2] ON [t2].[MStaffId] = [t1].[MStaffId]) ON 
[t1].[LId] = [t0].[LId] WHERE NOT ([t0].[IsActive] = 1) 
ORDER BY [t0].[LId], [t1].[LStaffId], [t2].[MStaffId]

(я сократил имена и добавил * на выбор).

Похоже, что с выбором все в порядке.

0 голосов
/ 20 октября 2010

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

var q = from p in db.Posts
        where db.Posts.Take(10).Contains(p)
        select p;

Использование DataLoadOptions с запросом выше даст вам первые десять сообщений, включая связанные с ними теги, все в одном запросе Результирующий SQL будет гораздо менее краткой версией следующего:

SELECT p.PostID, p.Title, pt.PostID, pt.TagID, t.TagID, t.Name FROM Posts p
JOIN PostsTags pt ON p.PostID = pt.PostID
JOIN Tags t ON pt.TagID = t.TagID
WHERE p.PostID IN (SELECT TOP 10 PostID FROM Posts)
0 голосов
/ 17 сентября 2008

Извините, нет, Eager Loading выполнит один дополнительный запрос для каждого тега на сообщение.

Проверено с этим кодом:

var options = new DataLoadOptions();
options.LoadWith<Post>(p => p.PostTags);
options.LoadWith<PostTag>(pt => pt.Tag);
using (var db = new BlogDataContext())
{
    db.LoadOptions = options;
    return (from p in db.Posts
            where p.Status != PostStatus.Closed
            orderby p.PublishDateGmt descending
            select p);
}

В примере базы данных он будет выполнять 4 запроса, что недопустимо в производстве. Кто-нибудь может предложить другое решение?

Спасибо

0 голосов
/ 17 сентября 2008

Я ответил на это в другом сообщении: О нетерпении загрузки . В вашем случае это, вероятно, будет что-то вроде:

DataLoadOptions options = new DataLoadOptions();    
options.LoadWith<Post>(p => p.PostTag);
options.LoadWith<PostTag>(pt => pt.Tag); 

Хотя будьте осторожны - DataLoadOptions должен быть установлен ДО ТОГО, как ЛЮБОЙ запрос отправляется в базу данных - если нет, выдается исключение (не знаю, почему это происходит в Linq2Sql - возможно, будет исправлено в более поздней версии). *

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