6

私は次の(かなり標準的な)テーブル構造を持っています:

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

つまり、最初の投稿には 2 つのタグがあり、2 番目の投稿には 1 つ、3 番目の投稿にはタグがありません。

すべての投稿とそのタグを 1 つのクエリで読み込みたいのですが、適切な演算子の組み合わせを見つけることができませんでした。タグのみの投稿、またはタグが複数ある場合の繰り返しの投稿のいずれかを読み込むことができました。

上記のデータベースから、Post オブジェクトのコレクション プロパティで 3 つの投稿とそのタグ (存在する場合) を取得したいと考えています。それはまったく可能ですか?

ありがとう

4

6 に答える 6

2

わーい!出来た。

誰かが同じ問題を抱えている場合、ここに私がやったことがあります:

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

これにより、2 つのクエリのみが実行され、各投稿のすべてのタグが読み込まれます。

アイデアは、必要な最後の投稿でクエリを制限するための値を取得し (この場合、PublishDateGmt 列で十分です)、Take() の代わりにその値で 2 番目のクエリを制限することです。

sirrocco さん、ご協力ありがとうございます。

于 2008-09-17T14:24:22.213 に答える
1

ごめんなさい。あなたが与える解決策はうまくいきますが、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 + NxMのクエリが生成されます。

だから、私の質問は今だと思います:レコードをページ付けするためのTake()メソッドの代わりはありますか?

ありがとう

于 2008-09-17T13:46:53.473 に答える
1

ちょっと不思議なので

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]

(名前を短くし、選択に * を追加しました)。

したがって、選択は問題ないようです。

于 2008-09-17T06:53:28.863 に答える
0

私は別の投稿でこれに答えました:熱心な読み込みについて。あなたの場合、それはおそらく次のようなものになるでしょう:

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

注意してください-DataLoadOptionsは、クエリがデータベースに送信される前に設定する必要があります-そうでない場合、例外がスローされます(Linq2Sqlでこのようになる理由はわかりません-おそらく後のバージョンで修正されるでしょう)。

于 2008-09-17T04:26:56.440 に答える
0

申し訳ありませんが、Eager Loadingは、投稿ごとにタグごとに1つの追加クエリを実行します。

このコードでテスト済み:

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つのクエリを実行します。誰かが別の解決策を提案できますか?

ありがとう

于 2008-09-17T04:35:02.083 に答える
0

これが古い投稿であることは承知していますが、クエリを 1 つだけ実行しながら Take() を使用する方法を発見しました。秘訣は、ネストされたクエリ内で Take() を実行することです。

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

上記のクエリで DataLoadOptions を使用すると、関連するタグを含む最初の 10 件の投稿がすべて 1 つのクエリで取得されます。結果として得られる 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)
于 2010-10-20T17:42:41.083 に答える