0

関連するテーブルからいくつかの集計情報を返す複雑なクエリを実行する必要があるコードファーストデータモデルがあります。最初に私の簡略化されたモデル:

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コレクションに対していくつかのループを使用することでした。これは問題なく機能しますが、パフォーマンスはひどいものです。

これを修正するために、groupbyとsum/ maxの集計を使用して、3つのテーブルで左結合を実行するSQLクエリを作成しました。このSQLはcontext.Database.SqlQuery(sql);で実行されます。データは正常に入力され、パフォーマンスは非常に良好です。

私の質問は次のとおりです。

  1. 結果セットをループしてcontext.Usersから手動で入力することなく、ForumWithの「intlastUser」を実際のユーザーオブジェクト「UserlastUserObject」に置き換える方法はありますか。

  2. パフォーマンスを維持しながら、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 
4

1 に答える 1

2

これを実現するための Linq 2 EF の方法がいくつかあります。以下に示す最初のものは、おそらく 2 つの中で最も遅いですが、スキーマと入力されたデータがなければ、私には確信が持てません。両方とも、リストにフォーラムの入力済みリストが含まれていることを前提としています (ほとんどの場合、コンテキスト、つまり 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
        };
于 2013-02-20T09:53:08.593 に答える