我有以下(相当标准的)表结构:
1
| Post <-> PostTag <-> Tag |
假设我有以下记录:
1 2 3 4 5 6 7 8 9 10 11 12 13
| PostID Title
1, 'Foo'
2, 'Bar'
3, 'Baz'
TagID Name
1, 'Foo'
2, 'Bar'
PostID TagID
1 1
1 2
2 2 |
换句话说,第一个帖子有两个标签,第二个帖子有一个标签,而第三个没有任何标签。
我想在一个查询中加载所有帖子及其标签,但无法找到合适的运算符组合。 我已经可以只加载带有标签的帖子,也可以加载多个标签时的重复帖子。
给定上面的数据库,我想在Post对象的collection属性中接收三个帖子及其标签(如果有)。 有可能吗?
谢谢
好极了!有效。
如果有人遇到相同的问题,这就是我所做的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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()限制第二个查询。
感谢您的帮助sirrocco。
对不起。您提供的解决方案有效,但是我发现在使用Take(N)分页时,它会中断。我正在使用的完整方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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 2 3 4 5 6 7 8 9
| 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 ( ); |
结果如下:
1 2 3 4 5 6 7 8 9 10 11
| 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] |
(我缩短了名称,并在选择项上添加了*)。
这样看来就可以了。
我知道这是一篇旧文章,但是我发现了一种仅执行一个查询的同时使用Take()的方法。诀窍是在嵌套查询中执行Take()。
1 2 3
| var q = from p in db.Posts
where db.Posts.Take(10).Contains(p)
select p; |
在上面的查询中使用DataLoadOptions将在一个查询中为您提供前十个帖子,包括它们的关联标签。生成的SQL将是以下内容的简洁得多:
1 2 3 4
| 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) |
很抱歉,不行,Eager Loading将为每个帖子的每个标签执行一个额外的查询。
经过以下代码测试:
1 2 3 4 5 6 7 8 9 10 11
| 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个在生产中不可接受的查询。谁能建议其他解决方案?
谢谢
我已经在另一篇文章中回答了这个问题:关于渴望加载。在您的情况下,可能类似于:
1 2 3
| DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Post>(p => p.PostTag);
options.LoadWith<PostTag>(pt => pt.Tag); |
尽管要小心-在将任何查询发送到数据库之前必须先设置DataLoadOptions-否则,将引发异常(不知道为什么在Linq2Sql中这样子-可能会在以后的版本中修复)。