載入相關實體
Entity Framework 支援三種方式來載入相關資料 - 積極式載入、延遲載入和明確載入。 本主題所示範的技巧同樣適用於使用 Code First 和 EF 設計工具所建立的模型。
急切載入
積極式載入是一種程式,其中一種實體的查詢也會在查詢過程中載入相關的實體。 使用 Include 方法可達成積極式載入。 例如,下列查詢會載入部落格,以及與每個部落格相關的所有文章。
using (var context = new BloggingContext())
{
// Load all blogs and related posts.
var blogs1 = context.Blogs
.Include(b => b.Posts)
.ToList();
// Load one blog and its related posts.
var blog1 = context.Blogs
.Where(b => b.Name == "ADO.NET Blog")
.Include(b => b.Posts)
.FirstOrDefault();
// Load all blogs and related posts
// using a string to specify the relationship.
var blogs2 = context.Blogs
.Include("Posts")
.ToList();
// Load one blog and its related posts
// using a string to specify the relationship.
var blog2 = context.Blogs
.Where(b => b.Name == "ADO.NET Blog")
.Include("Posts")
.FirstOrDefault();
}
注意
Include 是 System.Data.Entity 命名空間中的擴充方法,因此請確定您使用該命名空間。
急切地載入多個層級
您也可以急切地載入多個相關實體層級。 下列查詢顯示如何針對集合和參考導覽屬性執行這項操作的範例。
using (var context = new BloggingContext())
{
// Load all blogs, all related posts, and all related comments.
var blogs1 = context.Blogs
.Include(b => b.Posts.Select(p => p.Comments))
.ToList();
// Load all users, their related profiles, and related avatar.
var users1 = context.Users
.Include(u => u.Profile.Avatar)
.ToList();
// Load all blogs, all related posts, and all related comments
// using a string to specify the relationships.
var blogs2 = context.Blogs
.Include("Posts.Comments")
.ToList();
// Load all users, their related profiles, and related avatar
// using a string to specify the relationships.
var users2 = context.Users
.Include("Profile.Avatar")
.ToList();
}
注意
目前無法篩選要載入哪些相關實體。 Include 一律會帶入所有相關實體。
消極式載入
延遲載入是指第一次存取參考實體/實體的屬性時,從資料庫自動載入實體或實體集合的程式。 使用 POCO 實體類型時,藉由建立衍生 Proxy 類型的實例,然後覆寫虛擬屬性以新增載入攔截,即可達成延遲載入。 例如,使用下面定義的 Blog 實體類別時,第一次存取貼文導覽屬性時,將會載入相關的貼文:
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Tags { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
關閉延遲載入以進行序列化
延遲載入和序列化不會很好地混合,如果您不小心,您最終可以查詢整個資料庫,只是因為已啟用延遲載入。 大部分序列化程式的運作方式是存取類型實例上的每個屬性。 屬性存取會觸發延遲載入,因此會序列化更多實體。 在這些實體屬性上,會存取更多實體。 在序列化實體之前,先關閉延遲載入是個不錯的做法。 下列各節說明如何執行這項操作。
關閉特定導覽屬性的延遲載入
將 Posts 屬性設為非虛擬,即可關閉貼文集合的延遲載入:
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Tags { get; set; }
public ICollection<Post> Posts { get; set; }
}
使用積極式載入仍然可以使用積極式載入來載入貼文集合(請參閱 上方急切 載入)或 Load 方法(請參閱 下面的明確載入 )。
關閉所有實體的延遲載入
在 Configuration 屬性上設定旗標,即可關閉內容中所有實體的延遲載入。 例如:
public class BloggingContext : DbContext
{
public BloggingContext()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
使用積極式載入仍然可以使用積極式載入來載入相關實體(請參閱 上方急切 載入)或 Load 方法(請參閱 下面的明確載入 )。
明確載入
即使停用延遲載入,仍可能延遲載入相關的實體,但必須透過明確的呼叫來完成。 若要這樣做,您可以在相關實體的專案上使用 Load 方法。 例如:
using (var context = new BloggingContext())
{
var post = context.Posts.Find(2);
// Load the blog related to a given post.
context.Entry(post).Reference(p => p.Blog).Load();
// Load the blog related to a given post using a string.
context.Entry(post).Reference("Blog").Load();
var blog = context.Blogs.Find(1);
// Load the posts related to a given blog.
context.Entry(blog).Collection(p => p.Posts).Load();
// Load the posts related to a given blog
// using a string to specify the relationship.
context.Entry(blog).Collection("Posts").Load();
}
注意
當實體具有另一個單一實體的導覽屬性時,應該使用 Reference 方法。 另一方面,當實體具有其他實體集合的導覽屬性時,應該使用 Collection 方法。
明確載入相關實體時套用篩選
Query 方法可讓您存取 Entity Framework 載入相關實體時將使用的基礎查詢。 接著,您可以使用 LINQ 將篩選套用至查詢,再透過呼叫 ToList、Load 等 LINQ 擴充方法來執行查詢。Query 方法可以搭配參考和集合導覽屬性使用,但對集合而言最有用,因為集合可用來只載入集合的一部分。 例如:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
// Load the posts with the 'entity-framework' tag related to a given blog.
context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Where(p => p.Tags.Contains("entity-framework"))
.Load();
// Load the posts with the 'entity-framework' tag related to a given blog
// using a string to specify the relationship.
context.Entry(blog)
.Collection("Posts")
.Query()
.Where(p => p.Tags.Contains("entity-framework"))
.Load();
}
使用 Query 方法時,最好關閉流覽屬性的延遲載入。 這是因為在執行篩選查詢之前或之後,延遲載入機制可能會自動載入整個集合。
注意
雖然關聯性可以指定為字串而非 Lambda 運算式,但當使用字串時,傳回的 IQueryable 不是泛型的,因此通常需要 Cast 方法,才能使用它完成任何有用的動作。
使用查詢來計算相關實體而不載入它們
有時候,知道有多少實體與資料庫中的另一個實體相關,而不會實際產生載入所有這些實體的成本會很有用。 使用 LINQ Count 方法的 Query 方法可用來執行這項操作。 例如:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
// Count how many posts the blog has.
var postCount = context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Count();
}