如何使用 Linq 对两个以上的表进行分组
How to group more than two tables using Linq
我有三个表("Ratings"、"Comments" 和 "Users")。一个评级可以有多个评论。一条评论属于一个用户。
到目前为止,我的 LINQ 语句运行良好。
from rating in Ratings
join comment in Comments
on rating.ID equals comment.RatingID
join user in Users
on comment.UserID equals user.ID
select new {rating, comment, user}
.Where(x => x.rating.LocationID == 3);
如何分组?
这在一定程度上取决于您要分组的依据。但是有多种解决方案。
解决方案一:
假设您想按评分分组,那么您可以这样做:
var query1 = from rating in db.Ratings
join comment in db.Comments
on rating.ID equals comment.RatingID
join user in db.Users
on comment.UserID equals user.ID
group new { comment, user } by rating into g
select new { g.Key, l = g.ToList() };
foreach (var row in query1)
{
// you get rows grouped by rating
Debug.WriteLine(row.Key.ID); // rating.ID
// and a list of comments/users per rating
foreach (var g in row.l)
{
Debug.WriteLine(g.comment.ID);
Debug.WriteLine(g.user.ID);
}
}
这会为您提供每个评分一行。并且动态对象 g
包含每个评分 comment/user 对的列表。
方案二:
但是,就像@gertarnold 提到的那样,只读入要分组的对象会更容易。然后遍历它的属性。像这样:
var query2 = db.Ratings;
foreach (var rating in query2)
{
Debug.WriteLine(rating.name);
foreach (var comment in rating.Comments)
{
Debug.WriteLine(comment.name);
Debug.WriteLine(comment.User.name);
}
}
这更容易理解。它的性能缺点很严重,因为它对内部循环中的每个评论执行单独的数据库 SELECT
语句。如果评级有很多评论,那么这会很慢。使用分组的第一个示例将所有内容都拉入单个数据库 SELECT
语句中,这要快得多。
方案三:
并且有一种方法可以兼顾两种解决方案的优点。像解决方案 2 中那样做,并在其前面添加一些 DataLoadOptions
:
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Rating>(rating => rating.Comments);
options.LoadWith<Comment>(comment => comment.User);
db.LoadOptions = options;
这将在单个 SELECT
中预加载所有评级和所有必要的子对象。它快速且易于阅读和理解。
PS: 附注:表应该以单数形式命名。在那种情况下 Rating
、Comment
和 User
而不是 Ratings
、Comments
和 Users
.
PS2:要在解决方案 1 中也获得没有评论的评级,您需要将连接转换为外部连接。像这样:
var query1 = from rating in db.Ratings
join comment in db.Comments
on rating.ID equals comment.RatingID into j1
from comment in j1.DefaultIfEmpty()
join user in db.Users
on comment.UserID equals user.ID into j2
from user in j2.DefaultIfEmpty()
group new { comment, user } by rating into g
select new { g.Key, l = g.ToList() };
我有三个表("Ratings"、"Comments" 和 "Users")。一个评级可以有多个评论。一条评论属于一个用户。
到目前为止,我的 LINQ 语句运行良好。
from rating in Ratings
join comment in Comments
on rating.ID equals comment.RatingID
join user in Users
on comment.UserID equals user.ID
select new {rating, comment, user}
.Where(x => x.rating.LocationID == 3);
如何分组?
这在一定程度上取决于您要分组的依据。但是有多种解决方案。
解决方案一:
假设您想按评分分组,那么您可以这样做:
var query1 = from rating in db.Ratings
join comment in db.Comments
on rating.ID equals comment.RatingID
join user in db.Users
on comment.UserID equals user.ID
group new { comment, user } by rating into g
select new { g.Key, l = g.ToList() };
foreach (var row in query1)
{
// you get rows grouped by rating
Debug.WriteLine(row.Key.ID); // rating.ID
// and a list of comments/users per rating
foreach (var g in row.l)
{
Debug.WriteLine(g.comment.ID);
Debug.WriteLine(g.user.ID);
}
}
这会为您提供每个评分一行。并且动态对象 g
包含每个评分 comment/user 对的列表。
方案二:
但是,就像@gertarnold 提到的那样,只读入要分组的对象会更容易。然后遍历它的属性。像这样:
var query2 = db.Ratings;
foreach (var rating in query2)
{
Debug.WriteLine(rating.name);
foreach (var comment in rating.Comments)
{
Debug.WriteLine(comment.name);
Debug.WriteLine(comment.User.name);
}
}
这更容易理解。它的性能缺点很严重,因为它对内部循环中的每个评论执行单独的数据库 SELECT
语句。如果评级有很多评论,那么这会很慢。使用分组的第一个示例将所有内容都拉入单个数据库 SELECT
语句中,这要快得多。
方案三:
并且有一种方法可以兼顾两种解决方案的优点。像解决方案 2 中那样做,并在其前面添加一些 DataLoadOptions
:
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Rating>(rating => rating.Comments);
options.LoadWith<Comment>(comment => comment.User);
db.LoadOptions = options;
这将在单个 SELECT
中预加载所有评级和所有必要的子对象。它快速且易于阅读和理解。
PS: 附注:表应该以单数形式命名。在那种情况下 Rating
、Comment
和 User
而不是 Ratings
、Comments
和 Users
.
PS2:要在解决方案 1 中也获得没有评论的评级,您需要将连接转换为外部连接。像这样:
var query1 = from rating in db.Ratings
join comment in db.Comments
on rating.ID equals comment.RatingID into j1
from comment in j1.DefaultIfEmpty()
join user in db.Users
on comment.UserID equals user.ID into j2
from user in j2.DefaultIfEmpty()
group new { comment, user } by rating into g
select new { g.Key, l = g.ToList() };