載入相關實體

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();
}