Ładowanie powiązanych jednostek

Platforma Entity Framework obsługuje trzy sposoby ładowania powiązanych danych — chętne ładowanie, leniwe ładowanie i jawne ładowanie. Techniki przedstawione w tym temacie dotyczą modeli utworzonych przy użyciu podejścia „najpierw kod” i narzędzia EF Designer.

Z niecierpliwością ładują

Chętne ładowanie to proces, w którym zapytanie dla jednego typu jednostki ładuje również powiązane jednostki w ramach zapytania. Ładowanie chętne jest osiągane przy użyciu metody Include. Na przykład poniższe zapytania będą ładować blogi i wszystkie wpisy powiązane z każdym blogiem.

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

Uwaga

Include to metoda rozszerzenia w przestrzeni nazw System.Data.Entity, dlatego upewnij się, że używasz tej przestrzeni nazw.

Z niecierpliwością ładują wiele poziomów

Można również chętnie załadować wiele poziomów powiązanych jednostek. W poniższych zapytaniach przedstawiono przykłady tego, jak to zrobić zarówno dla właściwości nawigacji kolekcji, jak i odwołań.

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

Uwaga

Obecnie nie można filtrować, które powiązane jednostki są ładowane. Dołączanie zawsze spowoduje wprowadzenie wszystkich powiązanych jednostek.

Ładowanie opóźnione

Ładowanie opóźnione to proces, w którym jednostka lub kolekcja jednostek jest automatycznie ładowana z bazy danych przy pierwszym uzyskiwaniu dostępu do właściwości odwołującej się do jednostki/jednostek. W przypadku korzystania z typów jednostek POCO ładowanie z opóźnieniem jest osiągane przez utworzenie wystąpień pochodnych typów serwera proxy, a następnie zastąpienie właściwości wirtualnych w celu dodania haka ładowania. Na przykład w przypadku korzystania z poniższej klasy jednostki Blog powiązane wpisy zostaną załadowane przy pierwszym uzyskiwaniu dostępu do właściwości nawigacji Posty:

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

Wyłączanie ładowania z opóźnieniem na potrzeby serializacji

Ładowanie z opóźnieniem i serializacji nie mieszają się dobrze, a jeśli nie jesteś ostrożny, możesz wykonać zapytanie dla całej bazy danych tylko dlatego, że jest włączone ładowanie leniwe. Większość serializatorów działa przez uzyskanie dostępu do każdej właściwości w wystąpieniu typu. Dostęp do właściwości wyzwala ładowanie leniwe, więc więcej jednostek jest serializowanych. Dostęp do tych właściwości jednostek jest uzyskiwany, a jeszcze więcej jednostek jest ładowanych. Dobrym rozwiązaniem jest wyłączenie ładowania z opóźnieniem przed serializacji jednostki. W poniższych sekcjach pokazano, jak to zrobić.

Wyłączanie ładowania z opóźnieniem dla określonych właściwości nawigacji

Ładowanie leniwe kolekcji Posty można wyłączyć, tworząc właściwość Posty niewirtualnie:

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

Ładowanie kolekcji Postów można nadal osiągnąć przy użyciu chętnego ładowania (zobacz Chętne ładowanie powyżej) lub metody Load (zobacz Jawne ładowanie poniżej).

Wyłącz ładowanie leniwe dla wszystkich jednostek

Ładowanie z opóźnieniem można wyłączyć dla wszystkich jednostek w kontekście, ustawiając flagę we właściwości Configuration. Przykład:

public class BloggingContext : DbContext
{
    public BloggingContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
}

Ładowanie powiązanych jednostek można nadal osiągnąć przy użyciu chętnego ładowania (zobacz Chętne ładowanie powyżej) lub metody Load (zobacz Jawne ładowanie poniżej).

Jawne ładowanie

Nawet z opóźnieniem ładowania jest nadal możliwe lazily załadować powiązane jednostki, ale należy to zrobić za pomocą jawnego wywołania. W tym celu należy użyć metody Load we wpisie powiązanej jednostki. Na przykład:

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

Uwaga

Metoda Reference powinna być używana, gdy jednostka ma właściwość nawigacji do innej pojedynczej jednostki. Z drugiej strony metoda Collection powinna być używana, gdy jednostka ma właściwość nawigacji do kolekcji innych jednostek.

Metoda Query zapewnia dostęp do bazowego zapytania, które będzie używane przez program Entity Framework podczas ładowania powiązanych jednostek. Następnie można użyć LINQ, aby zastosować filtry do zapytania przed wykonaniem go za pomocą wywołania metody rozszerzenia LINQ, takiej jak ToList, Load itp. Metoda Query może służyć zarówno z właściwościami nawigacji odwołania, jak i kolekcji, ale jest najbardziej przydatna w przypadku kolekcji, w których może służyć do ładowania tylko części kolekcji. Przykład:

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

W przypadku korzystania z metody Query najlepiej jest zwykle wyłączyć ładowanie leniwe dla właściwości nawigacji. Dzieje się tak dlatego, że w przeciwnym razie cała kolekcja może zostać załadowana automatycznie przez mechanizm ładowania z opóźnieniem lub po wykonaniu filtrowanego zapytania.

Uwaga

Chociaż relację można określić jako ciąg zamiast wyrażenia lambda, zwracany element IQueryable nie jest ogólny, gdy jest używany ciąg, dlatego metoda rzutowania jest zwykle potrzebna, zanim wszystko będzie przydatne.

Czasami warto wiedzieć, ile jednostek jest powiązanych z inną jednostką w bazie danych bez faktycznego ponoszenia kosztów ładowania wszystkich tych jednostek. W tym celu można użyć metody Query z metodą LINQ Count. Przykład:

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