簡體   English   中英

實體框架上的懶惰與急切加載性能

[英]Lazy vs eager loading performance on Entity Framework

所以我的DbContext上有以下模型類:

貸款

每次我渲染一個LoanApplication對象列表時,我都會這樣做:

var context = new MyContext();
var applications = context.LoanApplications.Where(d => d.PropertyThatIWantToFilter = localVariable);

這將返回一個IQueryable,然后我在我的控制器方法調用上轉換為這樣的ViewModel:

var vm = applications.Select(d => new LoanApplicationViewModel(d));

LoanApplicationViewModel構造函數內部,我接受實體對象並執行相應的映射。 問題在於,由於Solicitors集合是一個導航屬性,因此每次實例化新的視圖模型時都會對數據庫進行調用。 每個應用程序的平均律師數量是兩個,這意味着如果我呈現一個列出最后10個應用程序的表,那么該應用程序將大約18-20次訪問數據庫。

我認為必須有一個更好的方法來獲得這個集合,所以我改變了我的原始查詢,急切地加載集合,如下所示:

var applications = context.LoanApplications.Include("Solicitors").Where...

雖然這會將對數據庫的調用次數減少到只有一次, 但查詢速度要慢得多,大約慢50%。

數據庫托管在SQL Azure上,我們已經實現了瞬態錯誤處理,但我希望減少對數據庫的調用數量,而不會降低響應時間性能。

這里的最佳做法是什么?

“這里的最佳做法是什么?”

最好的做法是

  1. 設置!應用范圍廣! 績效目標
  2. 輪廓,基准和定位瓶頸
  3. 檢查並微調瓶頸,為您提供最佳性能,贏得最少的工作。 (根據我的經驗,90%的時間不是tsql)

現在看起來似乎有點無關緊要,但從這個角度來看,哪種加載模式你在應用程序域中最佳化是正確的方法。

渴望/懶惰沒有“最佳實踐”。 這就是為什么兩種選擇都可用的原因。 此外,如果tsql是你的瓶頸並且在渴望/懶惰之間切換仍然沒有達到你的性能目標,那么你需要在SSMS中查看大量其他工具,例如查詢分析器和查詢計划分析器。


對於某些背景:

我正在谷歌搜索“急切加載慢”來到這里。 這是我的結果:

var foo = _context.Foos
    //.Include("Answers")
    //.Include("Attachments")
    .FirstOrDefault(q => q.Id == key);

渴望加載:106ms

延遲加載:11ms + 5ms + 5ms

懶人裝載獲勝,故事結束。

除了在使用eager和lazy時產生巨大結果或大量調用的SQL語句之外,通過從結果中放入和映射到ObjectContext / DbContext中會發生巨大的工作。 這會導致巨大的性能損失,在檢索大量數據時我無法真正推薦任何這些。

最佳解決方案是指定顯式Select調用。 但是,在不知道如何構建viewmodel對象的情況下,給出一個如何執行此操作的示例有點困難。 所以,我在這里做的是給你一個使用匿名對象作為查詢結果的例子。

此示例為您提供有關聯系人所屬客戶的信息的聯系人。

var contacts = context.Contacts.Where(row => row.CategoryId == 1)
                      .Select(row => new {
                                             ContactId = row.Id,
                                             Name = row.Name,
                                             CustomerName = row.Customer.Name
                                         }).ToList();

此查詢將生成一個SQL SELECT,它使用內部聯接將Customer與Customer連接,然后僅選擇Contact.Id,Contact.Name和Customer.Name列。

如果您不打算使用數據並將更改保存回相同的上下文,則此解決方案是從服務器檢索數據的最有效方法。 它既不使用急切也不延遲加載。

如果你能以某種方式查詢你的律師表並使用你已經獲取的應用程序列表過濾查詢,那么獲取的實體將被緩存在你的上下文中,我相信這將被用於導航屬性而不是命中數據庫。

我不確定如何編寫律師提取查詢,但我在想這樣的事情

int[] applicationIDs = applications.Select(x => x.ID).ToArray();
var solicitors = context.Solicitors.Where(x => x.Applications.Any(y => applicationIDs.Contains(y.ID))).ToArray(); // added toarray to cause execution cause im never sure when the LINQ actually runs

你考慮過使用sql視圖嗎?

我不太確定Sql Azure。 但是在sql server中,在沒有正確索引的情況下連接2個表時可能會有性能損失。 也許這會發生在您的查詢中。

需要注意的是,您之前的查詢是使用where子句,2個調用訪問1個表。 在after查詢中,它使用where子句,1個調用訪問2個表。 在您的查詢后加入,可能需要不同的索引。

您可以創建一個sql視圖以確保使用正確的索引。 然后讓您的應用程序調用視圖。 存儲過程也可用於此目的,但不太適合這種情況。

急切加載提取冗余主數據。 盡管上下文中的對象圖僅存儲每個實體的單個主數據,但它會占用大量內存,但SQL會將大量數據轉儲到其中。 我從這里拍了下面的圖片

在此輸入圖像描述

如果你看到,Data of User表也在SQL查詢的結果集中重復了UserDetails表的數量。 這似乎可以區分性能因素(在您的情況下,主列有更多記錄,然后是詳細信息表)。

如果性能是您主要考慮的問題,我建議您在使用相同的where子句時使用LINQ join ,同時單獨獲取詳細信息表的數據。所以在您的情況下: -

步驟1

 var context = new MyContext();
    var applications = context.LoanApplications.Where(d => d.PropertyThatIWantToFilter = localVariable);

然后是step2

var solicitors = from s in context.Solicitors
join loanApp in context.LoanApplications
select s.columns
where loanApp. <<Same condition as in step 1 where clause>>

謝謝,你的問題讓我回顧了我自己的代碼:-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM