我有一个代码第一个数据模型,我需要做一个复杂的查询,从相关的表返回一些聚合信息。首先是我的简化模型:
public class Forum
{
public virtual int ForumID { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Topic> Topics { get; set; }
}
[NotMapped]
public class ForumWith : Forum
{
public virtual int topics { get; set; }
public virtual int messages { get; set; }
public virtual DateTime lastDate { get; set; }
public virtual int lastUser { get; set; }
}
public class Topic
{
public virtual int TopicID { get; set; }
public virtual string Subject { get; set; }
public virtual int ForumID { get; set; }
[ForeignKey("ForumID")]
public virtual Forum Forum { get; set; }
public virtual ICollection<Message> Messages { get; set; }
}
public class Message
{
public virtual int MessageID { get; set; }
public virtual int TopicID { get; set; }
[ForeignKey("TopicID")]
public virtual Topic Topic { get; set; }
public virtual string Text { get; set; }
public virtual DateTime CreatedDate { get; set; }
public virtual User CreatedBy { get; set; }
}
我想要做的是使用上次创建的消息中的主题数,消息数以及日期和用户填充“ForumWith”对象。
我的第一个解决方案是在forum.topics和forum.topics.messages集合上使用几个循环。这很好用,但性能很糟糕。
为了解决这个问题,我创建了一个SQL查询,在3个表上进行左连接,包括group by和sum / max聚合。这个SQL我用context.Database.SqlQuery(sql)执行;并且数据填充得很好,性能非常好。
我的问题是:
有没有什么方法可以将ForumWith中的“int lastUser”替换为实际的用户对象“User lastUserObject”,而不必循环遍历结果集并从context.Users手动填充它。
有没有更好的方法通过linq / EF进行这种聚合,同时保持性能?
SQL:
SELECT dbo.Forum.ForumID ,
dbo.Forum.Name ,
COUNT(DISTINCT dbo.Topics.TopicID) AS topics,
COUNT(DISTINCT MessageID) AS messages,
MAX(dbo.Messages.CreatedDate) AS lastDate,
(SELECT UserName FROM [User] where UserId =
CONVERT(INT,SUBSTRING(max(CONVERT(varchar,dbo.Messages.CreatedDate,20) + '#' + CONVERT(VARCHAR,dbo.Messages.CreatedBy_UserId,20)),21,10))) AS lastUser
FROM dbo.Forum
LEFT JOIN dbo.Topics ON dbo.Forum.ForumID = dbo.Topics.ForumID
LEFT JOIN dbo.Messages ON dbo.Topics.TopicID = dbo.Messages.TopicID
GROUP BY dbo.Forum.ForumID ,
dbo.Forum.Name
答案 0 :(得分:2)
有几种Linq 2 EF方法可以实现这一目标。下面显示的第一个可能是两者中最慢的,但如果没有您的架构和填充数据,我无法确定。假设列表都包含填充的论坛列表(很可能会从您的上下文中提取,即dbContext.Forums)
方法1
list.Select(
x =>
new ForumWith
{
ForumID = x.ForumID,
Name = x.Name,
topics = x.Topics.Count,
messages = x.Topics.SelectMany(y => y.Messages).Count(),
lastDate = x.Topics.SelectMany(y => y.Messages).OrderByDescending(y => y.CreatedDate).First().CreatedDate,
lastUser = x.Topics.SelectMany(y => y.Messages).OrderByDescending(y => y.CreatedDate).First().CreatedBy
});
方法2
from forum in list
let latest = forum.Topics.SelectMany(x => x.Messages).OrderByDescending(x => x.CreatedDate).First()
select
new ForumWith
{
ForumID = forum.ForumID,
Name = forum.Name,
topics = forum.Topics.Count,
messages = forum.Topics.SelectMany(y => y.Messages).Count(),
lastDate = latest.CreatedDate,
lastUser = latest.CreatedBy
};