LINQ Lambda多重分组连接

5

我正在尝试获取产品列表以及它们的评分、评论和浏览量。PID是没有外键关系的产品ID列。

产品 -

Id  Name
1   P1
2   P2

评分 -

Id  PID Rating
1   1   5
2   1   4
3   2   3

评论 -

Id  PID Comment
1   1   Good
2   1   Average
3   2   Bad

Views -

Id  PID View
1   1   500
2   1   200
3   2   10

我的课程大纲如下 -
Public Class Product{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Rating> Ratings{ get; set; }
    public List<Comments> Comments{ get; set; }
    public List<Views> Views{ get; set; }
}

我正在尝试使用Linq分组连接获取这些信息,以便获得子集合。

IEnumerable<Product> _products = _context.Product.GroupJoin(_context.Rating, p=>p.id, r=>r.PID, (Product, Rating) => new Product(){
    //fill fields here
});

但是如何将其他表也组合到单个数据库查询中呢?
谢谢。

linq-to-sql 在提交更改时会自动处理查询的分组。你在问什么? - NH.
尝试这样的代码:IEnumerable<Product> _products = (from p in _context.Product join r in _context.Rating on p.id equals r.id Join s in _context.Sales on p.id equals s.id select new { p = p, r = r, s = s}).Select(x => new Product() { ..... } - jdweng
我想将结果以父子格式呈现,就像课堂上所提到的那样。这是我进行分组连接的区域。我猜想简单连接会将所有记录呈现为平面格式。如果我错了,请纠正我。 - sunder
2个回答

3

不必使用 GroupJoin,你可以直接查找匹配项来构造 Product 对象:

IEnumerable<Product> _products = _context.Product.Select(product => new Product() {
    Id = product.id,
    Name = product.name,
    Ratings = _context.Rating.Where(r => r.PID == product.id).ToList(),
    // ... other lists similar
});

如评论中所指出,上述查询可能会为每个产品生成三个子查询。

如果创建匿名对象来保存中间结果,则可以使用GroupJoin

var _products = _context.Product.GroupJoin(_context.Rating, p => p.id, r => r.PID, (p, rs) => new { p, rs })
                                .GroupJoin(_context.Comment, prs => prs.p.id, c => c.PID, (prs, cs) => new { prs.p, prs.rs, cs })
                                .GroupJoin(_context.View, prs => prs.p.id, v => v.PID, (prscs, vs) => new Product() {
                                    Id = prscs.p.id,
                                    Name = prscs.p.name,
                                    Ratings = prscs.rs.ToList(),
                                    Comments = prscs.cs.ToList(),
                                    Views = vs.ToList()
                                });

1
这可能是一个昂贵的操作。对于每个产品,它都会触发一个评分、查看和评论查询。 - sunder
不错的观点,但我很失望LINQ to SQL不够聪明,不能转换为单个查询? - NetMage
1
这个group join是有效的。但LINQPad生成的SQL太庞大了。实际上,看起来group join和inner query都生成了类似的SQL。它会针对每条产品记录触发一个评分、查看和评论查询。 - sunder
GroupJoin 没有生成 SQL JOIN - NetMage

0
你可以尝试这样做;
        var records = _context.Product
            .GroupJoin(_context.Ratings, p => p.Id, r => r.PID, (p, r) => new { Product = p, Ratings = r})
            .GroupJoin(_context.Comments, p => p.Product.Id, c => c.PID, (p, c) => new { p.Product, p.Ratings, Comments = c})
            .GroupJoin(_context.Views, p => p.Product.Id, v => v.PID, (p, v) => new { p.Product, p.Ratings, p.Comments, Views = v })
            .Select(p => new
            {
                Id = p.Product.Id,
                Name = p.Product.Name,
                Comments = p.Comments,
                Ratings = p.Ratings,
                Views = p.Views
            })
            .ToList().Select(x => new Product
            {
                Id = x.Id,
                Name = x.Name,
                Comments = x.Comments.ToList(),
                Ratings = x.Ratings.ToList(),
                Views = x.Views.ToList()
            }).ToList();

2
我相信第一个 ToList 生成的每个记录都会触发额外的 SQL 语句,这取决于返回集生成和执行数百到数千个 SQL 语句。 - David Sopko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接