Eager Loading zugehöriger Daten

Eager Loading

Sie können mit der Include-Methode zugehörige Daten angeben, die in den Abfrageergebnissen enthalten sein sollen. Im folgenden Beispiel weisen die in den Ergebnissen zurückgegebenen Blogs ihre Posts-Eigenschaft auf, die mit den zugehörigen Beiträgen aufgefüllt wurden.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ToList();
}

Tipp

Entity Framework Core korrigiert automatisch Navigationseigenschaften zur Korrektur für alle anderen Entitäten, die zuvor in die Kontextinstanz geladen wurden. Auch wenn Sie die Daten für eine Navigationseigenschaft nicht explizit eingeschlossen haben, kann die Eigenschaft folglich immer noch aufgefüllt werden, wenn einige oder alle zugehörigen Entitäten zuvor geladen wurden.

Sie können zugehörigen Daten aus mehreren Beziehungen in einer einzelnen Abfrage einschließen.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .Include(blog => blog.Owner)
        .ToList();
}

Achtung

Eager Loading einer Sammlungsnavigation in einer einzelnen Abfrage kann zu Leistungsproblemen führen. Weitere Informationen finden Sie unter Vergleich von Einzel- und geteilten Abfragen.

Einschließen mehrerer Ebenen

Sie können mit der ThenInclude-Methode einen Drilldown für Beziehungen ausführen, um mehrere Ebenen zugehöriger Daten einzuschließen. Im folgenden Beispiel werden sämtliche Blogs, die zugehörigen Beiträge und die Autoren der einzelnen Beiträge geladen.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ToList();
}

Sie können mehrere Aufrufe mit ThenInclude verbinden, um mit dem Einschließen weiterer Ebenen zugehöriger Daten fortzufahren.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
        .ToList();
}

Sie können alle diese Aufrufe kombinieren, um eine gemeinsame Abfrage für verknüpfte Daten aus mehreren Ebenen und Stämmen auszuführen.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .ThenInclude(author => author.Photo)
        .Include(blog => blog.Owner)
        .ThenInclude(owner => owner.Photo)
        .ToList();
}

Für eine der Entitäten, die eingeschlossen wird, sollten Sie mehrere zugehörige Entitäten einschließen. Beispiel: Beim Abfragen von Blogs schließen Sie Posts ein und möchten anschließend Author und Tags von Posts einschließen. Hierzu müssen Sie die einzelnen Include-Pfade beginnend beim Stamm angeben. Beispiel: Blog -> Posts -> Author und Blog -> Posts -> Tags. Dies bedeutet nicht, dass Sie redundante Verknüpfungen erhalten. In den meisten Fällen kombiniert EF die Verknüpfungen beim Generieren von SQL-Code.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags)
        .ToList();
}

Tipp

Sie können auch mehrere Navigationen mit einer einzelnen Include-Methode laden. Dies ist für „Navigationsketten“ möglich, die alle Verweise sind, oder wenn Sie mit einer einzelnen Sammlung enden.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Owner.AuthoredPosts)
        .ThenInclude(post => post.Blog.Owner.Photo)
        .ToList();
}

Gefilterte Include-Funktion

Wenn Sie „Include“ zum Laden zugehöriger Daten anwenden, können Sie bestimmte aufzählbare Vorgänge zur enthaltenen Sammlungsnavigation hinzufügen, mit denen Sie die Ergebnisse filtern und sortieren können.

Folgende Vorgänge werden unterstützt: Where, OrderBy, OrderByDescending, ThenBy, ThenByDescending, Skip und Take.

Solche Vorgänge sollten auf die Sammlungsnavigation in der Lambdafunktion angewendet werden, die an die Include-Methode übergeben wird, wie im folgenden Beispiel gezeigt:

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToList();
}

Jede per „Include“ eingeschlossene Navigation ermöglicht nur einen eindeutigen Satz von Filtervorgängen. In Fällen, in denen mehrere Include-Vorgänge für eine bestimmte Sammlungsnavigation angewendet werden (blog.Posts in den folgenden Beispielen), können Filtervorgänge nur für einen von ihnen angegeben werden:

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
        .ToList();
}

Alternativ können identische Vorgänge für jede Navigation angewendet werden, die mehrmals über „Include“ eingeschlossen wird:

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
        .ToList();
}

Achtung

Bei der Nachverfolgung von Abfragen können aufgrund einer Navigationskorrektur unerwartete Ergebnisse für gefilterte Include-Abfragen auftreten. Alle relevanten Entitäten, die zuvor abgefragt und in der Änderungsnachverfolgung gespeichert wurden, werden in den Ergebnissen der gefilterten Include-Abfrage angezeigt, auch wenn sie die Anforderungen des Filters nicht erfüllen. Verwenden Sie NoTracking-Abfragen, oder erstellen Sie DbContext neu, wenn Sie gefilterte Include-Abfragen in diesen Situationen nutzen.

Beispiel:

var orders = context.Orders.Where(o => o.Id > 1000).ToList();

// customer entities will have references to all orders where Id > 1000, rather than > 5000
var filtered = context.Customers.Include(c => c.Orders.Where(o => o.Id > 5000)).ToList();

Hinweis

Bei Nachverfolgungsabfragen gilt die Navigation, auf die die gefilterte Include-Funktion angewendet wurde, als geladen. Das bedeutet, dass EF Core nicht versucht, seine Werte mit explizitem Laden oder verzögertem Laden neu zu laden, auch wenn einige Elemente noch fehlen könnten.

Einschließen in abgeleiteten Typen

Mit Include und ThenInclude können Sie verknüpfte Daten aus einer Navigation einschließen, die nur für einen abgeleiteten Typ definiert wurde.

Betrachten Sie das folgende Modell:

public class SchoolContext : DbContext
{
    public DbSet<Person> People { get; set; }
    public DbSet<School> Schools { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<School>().HasMany(s => s.Students).WithOne(s => s.School);
    }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Student : Person
{
    public School School { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    public List<Student> Students { get; set; }
}

Inhalte der School-Navigation für alle Personen, die Studenten sind, können mit vielen Mustern vorzeitig geladen werden:

  • Mit der Umwandlung

    context.People.Include(person => ((Student)person).School).ToList()
    
  • Mit dem Operator as

    context.People.Include(person => (person as Student).School).ToList()
    
  • Mit einer Überladung von Include, die Parameter vom Typ string akzeptiert

    context.People.Include("School").ToList()
    

Modellkonfiguration für automatisches Einschließen von Navigationen

Sie können eine Navigation im Modell so konfigurieren, dass sie jedes Mal einbezogen wird, wenn die Entität mithilfe der AutoInclude-Methode aus der Datenbank geladen wird. Dies hat die gleiche Auswirkung wie die Angabe von Include mit der Navigation in jeder Abfrage, in der der Entitätstyp in den Ergebnissen zurückgegeben wird. Das folgende Beispiel zeigt, wie Sie eine Navigation so konfigurieren, dass sie automatisch eingeschlossen wird.

modelBuilder.Entity<Theme>().Navigation(e => e.ColorScheme).AutoInclude();

Nach der obigen Konfiguration wird durch das Ausführen einer Abfrage wie unten die ColorScheme-Navigation für alle Designs in den Ergebnissen geladen.

using (var context = new BloggingContext())
{
    var themes = context.Themes.ToList();
}

Diese Konfiguration wird auf jede Entität angewendet, die im Ergebnis zurückgegeben wird, unabhängig davon, wie sie in den Ergebnissen dargestellt wird. Wenn sich also eine Entität in den Ergebnissen befindet, da eine Navigation verwendet wird, werden durch Verwendung von Include über einen anderen Entitätstyp oder eine Konfiguration für automatisches Einschließen alle automatisch eingeschlossenen Navigationen für sie geladen. Dieselbe Regel gilt für die Navigationen, die für den abgeleiteten Typ der Entität als automatisch eingeschlossen konfiguriert sind.

Wenn Sie für eine bestimmte Abfrage die zugehörigen Daten nicht über eine Navigation laden möchten, die auf Modellebene so konfiguriert ist, dass sie automatisch eingeschlossen wird, können Sie die IgnoreAutoIncludes-Methode in Ihrer Abfrage verwenden. Wenn Sie diese Methode verwenden, werden nicht mehr alle Navigationen geladen, die vom Benutzer bzw. von der Benutzerin für das automatische Einschließen konfiguriert wurden. Wenn Sie eine Abfrage wie unten ausführen, werden alle Designs aus der Datenbank wieder aktiviert, ColorScheme wird jedoch nicht geladen, obwohl sie als automatisch eingeschlossene Navigation konfiguriert ist.

using (var context = new BloggingContext())
{
    var themes = context.Themes.IgnoreAutoIncludes().ToList();
}

Hinweis

Navigationen zu eigenen Typen werden standardmäßig auch als automatisch eingeschlossen konfiguriert, und die Verwendung der IgnoreAutoIncludes-API verhindert nicht, dass sie eingeschlossen werden. Sie werden weiterhin in den Abfrageergebnissen enthalten sein.