簡體   English   中英

實體框架6:虛擬集合懶加載甚至在查詢中顯式加載

[英]Entity Framework 6: virtual collections lazy loaded even explicitly loaded on a query

嘗試優化查詢時,EF6出現問題。 考慮此類與一個集合:

public class Client
{
    ... a lot of properties
    public virtual List<Country> Countries { get; set; }
}

如您所知,在EF嘗試獲取每個客戶的所有國家/地區時,使用延遲加載會遇到n + 1個問題。

我嘗試使用Linq投影; 例如:

        return _dbContext.Clients
            .Select(client => new
            {
                client,
                client.Countries
            }).ToList().Select(data =>
            {
                data.client.Countries = data.Countries; // Here is the problem
                return data.client;
            }).ToList();

在這里,我使用了兩個選擇:第一個用於Linq投影,以便EF可以創建SQL,第二個將結果映射到Client類。 這樣做的原因是因為我使用的是存儲庫接口,該接口返回List<Client>

盡管查詢是在其中包含國家/地區的情況下生成的,但是當我嘗試呈現整個信息時,EF仍在使用延遲加載(相同的n + 1問題)。 避免這種情況的唯一方法是刪除虛擬訪問器:

public class Client
{
    ... a lot of properties
    public List<Country> Countries { get; set; } 
}

該解決方案的問題在於,我們仍然希望將此屬性設置為虛擬。 此優化僅對於應用程序的特定部分是必需的,而在其他部分,我們希望具有此延遲加載功能。

我不知道如何將此屬性“通知” EF,該屬性已通過此Linq投影進行了延遲加載。 那可能嗎? 如果沒有,我們還有其他選擇嗎? n + 1問題使應用程序花費幾秒鍾來加載1000行。

編輯

感謝您的答復。 我知道我可以使用Include()擴展名來獲取集合,但是我的問題是我需要添加一些其他優化功能(對不起,我沒有發布完整的示例,我認為使用集合問題就足夠了):

public class Client
{
    ... a lot of properties
    public virtual List<Country> Countries { get; set; }
    public virtual List<Action> Actions { get; set; }
    public virtual List<Investment> Investments { get; set; }
    public User LastUpdatedBy {
        get {
          if(Actions != null) {
              return Actions.Last();
          }       
        }
    }
}

如果需要渲染客戶端,則需要使用Include()來獲取有關最近更新和投資數量( Count() )的信息,實際上我需要從數據庫中獲取所有信息。 但是,如果我使用像

        return _dbContext.Clients
              .Select(client => new
              {
                    client,
                    client.Countries,
                    NumberOfInvestments = client.Investments.Count() // this is translated to an SQL query
                    LastUpdatedBy = client.Audits.OrderByDescending(m => m.Id).FirstOrDefault(),
              }).ToList().Select(data =>
              {
                    // here I map back the data
                    return data.client;
              }).ToList();

我可以減少查詢,只獲取所需的信息( 在LastUpdatedBy的情況下,我需要將屬性更改為getter / setter,這不是什么大問題,因為它僅用於應用程序的此特定部分 )。

如果我將Select()與這種方法(投影然后是映射)一起使用,則EF不考慮Include()部分。

不知道要做什么,您使用的是lambda表達式而不是linq,而第二個選擇是不必要的。

data.clientclientdata.Countriesclient.Countries ,所以data.client.Countries = data.Countries送花兒給人真實的。

如果您不希望延遲加載Countries ,請使用_dbContext.Clients.Include("Countries").Where() or select ()

為了強制渴望加載虛擬屬性,您應該使用Include擴展方法。 這是MSDN https://msdn.microsoft.com/zh-cn/library/jj574232(v=vs.113).aspx的鏈接。

所以這樣的事情應該工作:

 return _dbContext.Clients.Include(c=>c.Countries).ToList();

如果我理解正確,可以嘗試一下

_dbContext.LazyLoading = false;

var clientWithCountres =  _dbContext.Clients
                                    .Include(c=>c.Countries)
                                    .ToList();

這將獲取Client ,只包括它Countries 如果禁用延遲加載,則不會從查詢中加載其他集合。 除非您指定包含或投影。

僅供參考:Projection和Include()不能一起使用,請參見以下答案:如果您正在投影,它將繞過include。 https://stackoverflow.com/a/7168225/1876572

我不是100%肯定,但是我認為您的問題是,您仍將維護內部查詢的可查詢性,直到查詢結束。

這個可查詢的是惰性的(因為在模型中它是惰性的),並且您沒有做任何解釋來說明情況並非如此,您只是將相同的惰性可查詢項投影到結果集中。

我無法直截了當地告訴您什么是正確的答案,但是我會嘗試以下方法:

1也使用內部可投影的投影,例如

return _dbContext.Clients
            .Select(client => new
            {
                client,
                Countries = client.Countries.Select(c=>c)// or a new Country
            })

2將include放在查詢的末尾(我很確定include應用於結果而不是輸入。如果將其放在投影之前,它肯定不起作用)例如:

_dbContext.Clients
            .Select(client => new
            {
                client,
                client.Countries
            }.Include(c=>c.Countries)`

3嘗試在投影內部指定枚舉,例如:

_dbContext.Clients
            .Select(client => new
            {
                client,
                Countries = client.Countries.AsEnumerable() //perhaps tolist if it works
            }`

我想通過說我還沒有嘗試以上任何方法來避免這種情況,但是我認為這將使您走上正確的道路。

關於延遲加載的注意事項

IMO很少有延遲加載的好用例。 除非您的用戶直接在模型上遵循惰性路徑,否則它幾乎總是會導致生成太多查詢。 僅在極其謹慎的情況下使用它,而在請求響應(例如Web)應用程序中則完全不使用IMO。

暫無
暫無

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

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