简体   繁体   English

EF自定义查询与相关表

[英]EF custom query with related tables

I have a code first data model where I need to do a complex query returning some aggregate information from related tables. 我有一个代码第一个数据模型,我需要做一个复杂的查询返回相关表中的一些聚合信息。 First my simplified model: 首先是我的简化模型:

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; }
} 

What I want to do is populate the "ForumWith" object with the number of topics, the number of messages, and thedate and user from the last created message. 我想要做的是使用上次创建的消息中的主题数,消息数以及日期和用户填充“ForumWith”对象。

My first solution to do this was using a couple of loops over the forum.topics and forum.topics.messages collection. 我的第一个解决方案是在forum.topics和forum.topics.messages集合上使用几个循环。 This works just fine, but the performance is abysmal. 这很好用,但性能很糟糕。

To fix this I've created a SQL query doing a left join on the 3 tables, with group by and sum/max aggregates. 为了解决这个问题,我创建了一个SQL查询,在3个表上进行左连接,包括group by和sum / max聚合。 This SQL i execute with context.Database.SqlQuery(sql); 这个SQL我用context.Database.SqlQuery(sql)执行; and the data is populated just fine, and the performance is very good. 并且数据填充得很好,性能非常好。

My questions are: 我的问题是:

  1. Is there any way I can replace the "int lastUser" from ForumWith with an actual user object "User lastUserObject" without having to loop over the result set and populate it manualy from the context.Users. 有没有什么办法可以将ForumWith中的“int lastUser”替换为实际的用户对象“User lastUserObject”,而不必循环遍历结果集并从context.Users手动填充它。

  2. Is there a better way to do this kind of aggregations through linq/EF while keeping the performance? 有没有更好的方法通过linq / EF进行这种聚合,同时保持性能?

SQL: 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 

There are a couple Linq 2 EF ways to accomplish this. 有几种Linq 2 EF方法可以实现这一目标。 The first shown below is likely the slowest of the two, but without your schema and populated data I can't be certain. 下面显示的第一个可能是两者中最慢的,但如果没有您的架构和填充数据,我无法确定。 Both assume list contains the populated list of Forums (most likely will be pulled from your context, ie dbContext.Forums) 假设列表都包含填充的论坛列表(很可能会从您的上下文中提取,即dbContext.Forums)

Method 1 方法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
                });

Method 2 方法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
        };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM