對象-關系映射框架比如EF有三種 方式使用 模型中的導航屬性來加載關聯數據。
一、.Lazy Loading.(關聯數據在訪問導航屬性時被透明的加載,不需要特別的代碼,自動的加載)
當一個實體第一次讀取的時候,關聯數據不會被檢索。然后,當你第一次訪問這個實體的導航屬性的時候,導航屬性需要的數據自動的被檢索。每當你第一次從導航屬性獲取數據時,都會向數據庫發送查詢。這樣,會導致多個查詢被發送到數據庫。一個是實體本身,另外,每當實體的關聯數據第一次被訪問的時候。EF6.x的DbContext類默認啟用了Lazy loading.
lazy loading 不支持AsNoTracking()方法,使用了.AsNoTracking(),lazy loading會發生異常。
而EF Core1的版本是不支持Lzay Loading的。從EF2.1.開始支持Lazy Loading,但必須是可以覆寫的導航屬性,也就是 說定義為虛 virtual屬性,和能夠被繼承的。需要進行以下設置。
1、安裝代理類
PMC:Install-Package Microsoft.EntityFrameworkCore.Proxies -Version 2.1.1
在Startup.cs配置文件中,ConfigureService方法中,
//啟動文件,在依賴注入容器中注冊數據庫上下文對象。
2、services.AddDbContext<SchoolContext>(options =>
options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); //如果未在Context中添加UseLazyLoadingProxies,導航屬性不會添加。
departments =context.Departments;
foreach(Department d in departments)
{
foreach(Course c in d.Courses)
{
courseLIst.Add(d.Name + c.title)
}
}
二、EagerLoading (關聯數據在初始的查詢里面就從數據庫加載)
當實體第一次被讀取的時候,關聯的數據一起被檢索。這將會導致僅有的連接查詢會檢索所有需要的全部數據。通過EF core使用Include方法和ThenInclude方法來指定eagerLoding.
departments =context.Departments.Include(x =>x.Course) //一條語句就從數據庫中加載所有的關聯屬性。
foreach(Department d in departments)
{
foreach(Course c in d.Courses)
{
courseList.Add(d.Name +c.Title);
}
}
三、Explicit loading (關聯數據后來才能被顯式的從數據庫加載)
1.這個與lazy loading相似,除了需要顯式的使用代碼來檢索關聯數據。當你訪問一個導航屬性的時候,不會自動發生。需要通過對象狀態管理器(object state manager)Entry手動的加載關聯數據,,調用Collection.Load(導航屬性為集合)方法和reference.Load(導航屬性為單個實體)方法。你使用顯式加載僅僅是關閉了lazy loading。
departments =context.Departments.Tolist();
foreach(Department d in departments)
{
context.Entry(d).Collection(d =>d.Courses).Load(); //用Entry load()代碼明確從數據庫加載關聯數據。
foreach(Course c in d.Courses)
{
courseList.Add(d.Name +c.Title);
}
}
2.你能夠使用單獨的查詢來檢索數據,EF會 修復導航屬性。也就是說EF自動地將它們所屬的單獨檢索的實體添加到先前檢索實體集的導航屬性中。對於檢索相關數據的查詢,可使用Load方法而不是返回列表或對象的方法,例如:ToList()或者 Single()
departments =context.Departments.Include(x =>x.Courses)
foreach(Department d in departments)
{
context.Courses.Where(c =>c.DepartmentID == d.DepartmentId).Load(); //從數據庫中加載實體的導航屬性,使用load();
foreach(Course c in d.Courses) // 由於EF Core修復了導航屬性,因此,這里不是lazy loading,而是eager loading.
{
courseList.Add(d.Name +c.Title)
}
}
因為lazy loading和explicit loading不會立即查詢屬性值,因為它們都屬於延遲加載(deferred loading).
性能考慮
如果每一個實體的導航屬性都需要被查詢,eager loading通常能提供最好的性能,因為只有一個查詢被發送到數據庫,比起檢索單個實體的獨立的查詢,減少了對數據庫訪問來回的次數,顯得更加高效。
另一方面,在一些情況下,Lazy loading更加高效。Eager loading可能導致非常復雜的連接查詢,SQL服務器不能有效的處理。如果你僅僅需要訪問一個實體集的其中一部分(子集)實體的導航屬性,則延遲加載可能會執行得更好,因為預先加載將檢索比您需要的更多數據。 如果性能至關重要,最好以兩種方式測試性能,以便做出最佳選擇。
延遲加載可以掩蓋導致性能問題的代碼。例如,處理大量的實體,並且在每次迭代中使用了多個導航屬性,沒有指定eager loading或explicit loading ,這樣的代碼執行效率非常低。因為多次往返數據庫。一個應用程序在本地開發過程中運行良好,但當移植到Azure SQL數據庫時,由於延遲增加和Lazying loading可能出現性能問題。
序列化 的時候使用lazy loading將導致失控的鏈式反應,序列化之前要關閉lazy loading.