我有一个查询我正在尝试从SQL(T-SQL)移植到LINQ到实体4.0(C#)。结果集包含标准“详细信息行”的组合以及聚合“统计信息”信息。
原始SQL使用标准选择左连接到聚合信息,类似于:
SELECT
UserId,
Name,
Email,
ISNULL(Stats.TotalPosts, 0) as TotalPosts,
Stats.LastPost
FROM Users
LEFT OUTER JOIN
(
SELECT UserId, COUNT(*) as TotalPosts, MAX(DatePosted) as LastPost
FROM Articles
GROUP BY UserId
) as Stats ON Stats.UserId = Users.UserID
出于性能原因,在SELECT语句中使用左连接而不是子查询 - 返回多个聚合统计信息(总帖子和上一篇文章的日期)
我在C#4.0中将它转换为LINQ-to-Entities查询有一些部分成功,但我不完全确定连接应该如何与group语句相关联。我想我正在考虑SQL,而不是正确使用LINQ。
我在将统计信息分解为单独的查询方面取得了一些成功:
var stats =
(
from a in entities.Articles
group a by a.UserId into g
select new
{
UserId = g.Key,
TotalPosts = g.Count(),
LastUpdated = g.Max(i => i.DatePosted)
}
);
var query =
(
from u in entities.Users
join s in stats on u.UserId equals s.UserId
orderby u.Name
select new UserListing()
{
UserId = u.UserId,
Name = u.Name,
Email = u.Email,
TotalPosts = s.TotalPosts,
LastUpdated = s.LastUpdated
}
);
不幸的是,LINQ查询中使用的联接会过滤掉所有未提交任何文章的用户。
通过包含DefaultIfEmpty切换到外部联接的等价物会导致其他问题 - 我只能为TotalPosts返回“null”而不是0.即使使用“TotalPosts =(s.TotalPosts == null)?0:s.TotalPosts “在select中,除非TotalPosts属性可以为空,否则抛出异常。
以这种方式组合详细信息行和汇总信息的最佳做法是什么?
谢谢!
答案 0 :(得分:1)
试试这个:
var query =
(
from u in entities.Users
join s in stats on u.UserId equals s.UserId into g
from a in g.DefaultIfEmpty()
orderby u.Name
select new UserListing()
{
UserId = u.UserId,
Name = u.Name,
Email = u.Email,
TotalPosts = a.TotalPosts,
LastUpdated = a.LastUpdated
}
);
答案 1 :(得分:1)
您拥有的一个选项是确保stats
查询中的相应属性可以为空。如果可能的话,LINQ到实体将进行必要的调整以使其工作。然后像往常一样执行左外连接。
var stats =
(
from a in entities.Articles
group a by a.UserId into g
select new
{
UserId = g.Key,
TotalPosts = (int?)g.Count(),
LastUpdated = g.Max(i => i.DatePosted)
}
);
var query =
(
from u in entities.Users
join s in stats on u.UserId equals s.UserId into joinedStats
from s in joinedStats.DefaultIfEmpty() // do left outer join
orderby u.Name
select new UserListing()
{
UserId = u.UserId,
Name = u.Name,
Email = u.Email,
TotalPosts = s.TotalPosts, // null if doesn't contain stats
LastUpdated = s.LastUpdated // default DateTime if doesn't contain stats
}
);
答案 2 :(得分:0)
要获得外部联接,您需要使用DefaultIfEmpty
。
要解决null问题,您可以尝试
TotalPosts = s.TotalPosts.GetValueOrDefault(),
或者如果s.TotalPosts以某种方式没有显示为int?
你可能会尝试像
TotalPosts = ((int?)s.TotalPosts).GetValueOrDefault(0),