Entity Framework 使用注意:Where查詢條件中用到的關聯實體不需要Include


來自博客園開發團隊開發前線最新消息:

在Entity Framework中,如果實體A關聯了實體B,你想在加載實體A的同時加載實體B。通常做法是在LINQ查詢中使用Include()。但是,如果你在查詢條件中用到了實體B,EF會自動加載實體B,這時Include不僅是多余的,而且還會增加額外的LEFT OUTER JOIN查詢,影響性能。 

請看我們在博問開發中遭遇這個問題時的一段代碼:

//For q.cnblogs.com
public class QuestionService 
{
    private IRepository<QuestionItem> _questionRepository;

    public QuestionService(IUnitOfWork unitOfWork)
        : base(unitOfWork)
    {
        _questionRepository = new Repository<QuestionItem>(unitOfWork);
    }

    public List<QuestionItem> GetUnsolvedQuestions(int pageIndex, int pageSize)
    {
        return _questionRepository.Entities
            .Include(q => q.User)
            .Where(q => q.IsActive && q.User.IsActive)
            .Skip((pageIndex - 1) * pageSize)
            .Take(pageSize)
            .ToList();
            
    }
}

public class QuestionItem
{
    public int Id { get;set; }
    public string Title { get; set; }
    public bool IsActive { get; set; }
    public int UserId { get; set; }

    public User User { get; set; }
}

public class User
{
    public int UserId { get; set; }
    public bool IsActive {get;set;}
}

在上面的代碼中,我們想在GetActiveQuestions()返回List<QuestionItem>時,QuestionItem中要包含User信息,所以在LINQ查詢使用了.Include(q => q.User)。

(特別要注意的是:這里把q.User.IsActive作為查詢條件之一)

然后我們用SQL Server Profiler發現,Entity Framework生成了如下的SQL語句:

SELECT TOP (25) 
[Filter1].[Id] AS [Id], 
[Filter1].[Title] AS [Title],
[Filter1].[UserId] AS [UserId], 
[Filter1].[UserId1] AS [UserId1], 
[Filter1].[IsActive1] AS [IsActive], 
[Filter1].[UserId2] AS [UserId1],
[Filter1].[IsActive2] AS [IsActive1]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title], 
[Extent1].[UserId] AS [UserId1],[Extent1].[IsActive] AS [IsActive1],
[Extent3].[UserID] AS [UserID2], [Extent3].[IsActive] AS [IsActive2], 
row_number() OVER (ORDER BY [Extent1].[QID] DESC) AS [row_number]
    FROM   [dbo].[question_Item] AS [Extent1]
    INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[UserID]
    LEFT OUTER JOIN [dbo].[Users] AS [Extent3] ON [Extent1].[UserID] = [Extent3].[UserID]
    WHERE ([Extent1].[IsActive] = 1) AND ([Extent2].[IsActive] = 1) 
)  AS [Filter1]
WHERE [Filter1].[row_number] > 0
ORDER BY [Filter1].[Id] DESC

[dbo].[Users]表對應的就是User實體類,上面的SQL中[dbo].[Users]出現了兩次JOIN,由於[Users]表數據量比較大,兩次JOIN影響了執行計划,查詢耗時增加。這顯然是要避免的。

在與這個問題一陣激戰之后,我們終於找到解決方法 —— 去掉Include,就這么簡單!

從這個地方看,Entity Framework還是挺聰明的,但是由於不知道它的這個聰明之處,反而帶來了問題。

所以,代碼如人,要和她相處好,就要了解她的一切!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM