我有一個名為的簡單類Company和相應的服務 ( CompanyService) 來訪問 Azure SQL 資料庫。一家公司包含Users。
public class Company
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(64, MinimumLength = 5)]
public string Name { get; set; }
public ICollection<User> Users { get; set; } = new List<User>(); // removing the initialization does not make any difference
}
public class User
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(64, MinimumLength = 5)]
public string Name { get; set; }
public Company Company { get; set; }
public int CompanyId { get; set; }
}
這兩個模型都添加到我的 DbContext 中,我可以進行簡單的 CRUD 操作。不管怎樣,我開始玩 UI 并想展示一家公司的用戶。所以基本上,你有一個包含所有公司的表格 -> 你選擇一個公司并單擊“編輯”按鈕 -> 一個新視圖打開,現在可以更新公司的屬性并顯示所有相應的用戶。
實施此操作后,我意識到我的Company.Users串列完全是空的,即使其中應該有 10 個虛擬用戶。我檢查了顯示所有用戶的視圖,并看到所有用戶都在那里。我再次導航到我的公司頁面,選擇了同一家公司,我看到了什么?本公司用戶!
那么問題出在哪里:用戶資料僅在我CompanyService從我的UserService. 我有 0 線索,為什么會這樣。
我像這樣訪問我的資料:
public async Task<Company?> GetCompanyById(int id)
{
return await this.Context.Company.Include(c => c.Users).FirstOrDefaultAsync(c => c.Id == id);
}
我的資料庫是這樣創建的:
builder.Services.AddDbContextFactory<DatabaseContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
uj5u.com熱心網友回復:
您實際上根本沒有使用延遲加載。要使延遲加載起作用,您需要將 Company.Users 集合宣告為virtual. 即使這樣,它也只會在公司仍在讀取它的 DbContext 范圍內時起作用。在傳遞物體時,這可能會使您絆倒。例如,當啟用延遲加載并且您在處理 DbContext 后嘗試訪問未加載的集合時,您將收到例外。
相反,您看到的是 EF 的物體跟蹤和參考人口行為。當您請求與其他物體有關系的物體時,您可以預先加載這些關系以確保加載和參考關聯的物體,或者將它們留給 EF 解決。EF 將自動關聯它碰巧已經在跟蹤的任何相關物體,無論您是否告訴它急切加載。
例如,假設我有一個父子關系。父 ID P1 有 3 個子 ID,ID C1、C3 和 C3。如果我使用以下陳述句,則禁用延遲加載:
var parent = context.Parents.Single(p => p.Id == "P1");
var count = parent.Children.Count();
我會得到“0”。(如果 Children 集合已初始化,否則我會得到 a NullReferenceException)如果我打開延遲加載,或者我像下面這樣急切加載:
var parent = context.Parents.Include(p => p.Children).Single(p => p.Id == "P1");
var count = parent.Children.Count();
在這兩種情況下,我都會得到“3”。
現在事情變得有趣了。如果延遲加載被禁用/兒童不是虛擬的,我會執行以下操作。
var child = context.Children.Single(c => c.Id == "C1");
var parent = context.Parents.Single(p => p.Id == "P1");
var count = parent.Children.Count();
我會得到“1”,而不是“0”,因為我沒有急切加載,而不是“3”,因為這里沒有應用延遲加載。如果恰好有 2 個相關的孩子被跟蹤,我會得到“2”。現在通常你不會做那么明顯的事情,但是任何“可能”已經將相關資料的跟蹤參考加載到稍后加載的行的任何早期代碼都將自動包含在該新行的關系中。這可能會導致令人困惑的錯誤,有時相關資料似乎可用但其他時候不可用,或者沒有完整的資料集可用。
這是您希望 DbContext 生命周期盡可能短的根本原因之一。DbContext 跟蹤的物體越多,“解決”被跟蹤物體之間可能的關系以填充參考所需的時間就越長,并且可能使用的潛在陳舊參考也就越多。(而不是從資料庫加載新的當前資料)
在避免此類問題時使用 EF 的最佳實踐:
- 讀取顯示資訊時,使用投影而不是加載物體。
Select即使用或 Automapper加載 ViewModelProjectTo。這避免了加載被跟蹤的物體并確保資料來自當前的持久資料狀態。它還構建了比加載具有急切加載關系的物體更有效的查詢。(因為 EF 在幕后構建笛卡爾產品,所以速度變慢了) - 如果您不需要更新資料但確實需要加載物體,請使用
AsNoTracking. 這避免了物體填充跟蹤快取,減慢速度并導致這些問題。 - 更新物體時,總是急切地加載需要更新的關系。理想地設計系統來更新獨立于父級的子級關系。即 AddChild、RemoveChild、UpdateChild,而不是對子集合進行更改并嘗試在單個 UpdateParent 中與所有其他更改一起更新它們。
- 確保 DbContext 的存活時間不會超過絕對需要的時間。長期存在的 DbContexts 收集昂貴的包袱。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/533077.html
標籤:C#网实体框架延迟加载
