Laden zugehöriger DatenLoading Related Data

Mit Entity Framework Core können Sie die Navigationseigenschaften in Ihrem Modell zum Laden zugehöriger Entitäten verwenden.Entity Framework Core allows you to use the navigation properties in your model to load related entities. Es gibt drei allgemeine O/RM-Muster für das Laden zugehöriger Daten.There are three common O/RM patterns used to load related data.

  • Eager Loading bedeutet, dass die zugehörigen Daten als Teil der ersten Abfrage aus der Datenbank geladen werden.Eager loading means that the related data is loaded from the database as part of the initial query.
  • Explizites Laden bedeutet, dass die zugehörigen Daten zu einem späteren Zeitpunkt explizit aus der Datenbank geladen werden.Explicit loading means that the related data is explicitly loaded from the database at a later time.
  • Lazy Loading bedeutet, dass die zugehörigen Daten bei Zugriff auf die Navigationseigenschaft transparent aus der Datenbank geladen werden.Lazy loading means that the related data is transparently loaded from the database when the navigation property is accessed.

Tipp

Das in diesem Artikel verwendete Beispiel finden Sie auf GitHub.You can view this article's sample on GitHub.

Eager LoadingEager loading

Sie können mit der Include-Methode zugehörige Daten angeben, die in den Abfrageergebnissen enthalten sein sollen.You can use the Include method to specify related data to be included in query results. 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.In the following example, the blogs that are returned in the results will have their Posts property populated with the related posts.

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.Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. 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.So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

Sie können zugehörigen Daten aus mehreren Beziehungen in einer einzelnen Abfrage einschließen.You can include related data from multiple relationships in a single query.

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

Einschließen mehrerer EbenenIncluding multiple levels

Sie können mit der ThenInclude-Methode einen Drilldown für Beziehungen ausführen, um mehrere Ebenen zugehöriger Daten einzuschließen.You can drill down thru relationships to include multiple levels of related data using the ThenInclude method. Im folgenden Beispiel werden sämtliche Blogs, die zugehörigen Beiträge und die Autoren der einzelnen Beiträge geladen.The following example loads all blogs, their related posts, and the author of each post.

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

Hinweis

Aktuelle Versionen von Visual Studio bieten falsche Codevervollständigungsoptionen an. Diese können verursachen, dass bei Verwendung der ThenInclude-Methode gemäß der Navigationseigenschaft einer Sammlung korrekte Ausdrücke mit Syntaxfehler gekennzeichnet werden.Current versions of Visual Studio offer incorrect code completion options and can cause correct expressions to be flagged with syntax errors when using the ThenInclude method after a collection navigation property. Dies ist ein Symptom für einen unter https://github.com/dotnet/roslyn/issues/8237 nachverfolgten IntelliSense-Fehler.This is a symptom of an IntelliSense bug tracked at https://github.com/dotnet/roslyn/issues/8237. Diese unbegründeten Syntaxfehler können ohne Bedenken ignoriert werden, solange der Code korrekt ist und erfolgreich kompiliert werden kann.It is safe to ignore these spurious syntax errors as long as the code is correct and can be compiled successfully.

Sie können mehrere Aufrufe mit ThenInclude verbinden, um mit dem Einschließen weiterer Ebenen zugehöriger Daten fortzufahren.You can chain multiple calls to ThenInclude to continue including further levels of related data.

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 all dies kombinieren, um zugehörige Daten aus mehreren Ebenen und mehreren Stämmen in derselben Abfrage einzuschließen.You can combine all of this to include related data from multiple levels and multiple roots in the same query.

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.You may want to include multiple related entities for one of the entities that is being included. Beispiel: Beim Abfragen von Blogs schließen Sie Posts ein und möchten anschließend Author und Tags von Posts einschließen.For example, when querying Blogs, you include Posts and then want to include both the Author and Tags of the Posts. Hierzu müssen Sie die einzelnen Includepfade beginnend beim Stamm angeben.To do this, you need to specify each include path starting at the root. Beispiel: Blog -> Posts -> Author und Blog -> Posts -> Tags.For example, Blog -> Posts -> Author and Blog -> Posts -> Tags. Dies bedeutet nicht, dass Sie redundante Verknüpfungen erhalten. In den meisten Fällen konsolidiert EF die Verknüpfungen beim Generieren von SQL.This does not mean you will get redundant joins, in most cases EF will consolidate the joins when generating SQL.

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

Einschließen in abgeleiteten TypenInclude on derived types

Sie können zugehörige Daten aus Navigationen einschließen, die nur in einem abgeleiteten Typ mit Include und ThenInclude definiert werden.You can include related data from navigations defined only on a derived type using Include and ThenInclude.

Betrachten Sie das folgende Modell:Given the following model:

    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 einer Reihe von Mustern vorzeitig geladen werden:Contents of School navigation of all People who are Students can be eagerly loaded using a number of patterns:

  • Mit castusing cast

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

    context.People.Include(person => (person as Student).School).ToList()
    
  • Mit einer Überladung von Include, die Parameter vom Typ string akzeptiertusing overload of Include that takes parameter of type string

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

Ignorierte Include-OperatorenIgnored includes

Wenn Sie die Abfrage so ändern, dass keine Instanzen des Entitätstyps mehr zurückgegeben werden, mit dem die Abfrage gestartet wurde, werden die Include-Operatoren ignoriert.If you change the query so that it no longer returns instances of the entity type that the query began with, then the include operators are ignored.

Im folgenden Beispiel basieren die Include-Operatoren auf dem Operator Blog. Die Abfrage wird jedoch dann mit dem Operator Select geändert, damit ein anonymer Typ zurückgegeben wird.In the following example, the include operators are based on the Blog, but then the Select operator is used to change the query to return an anonymous type. In diesem Fall haben die Include-Operatoren keine Auswirkungen.In this case, the include operators have no effect.

using (var context = new BloggingContext())
{
    var blogs = context.Blogs
        .Include(blog => blog.Posts)
        .Select(blog => new
        {
            Id = blog.BlogId,
            Url = blog.Url
        })
        .ToList();
}

EF Core protokolliert standardmäßig eine Warnung, wenn Include-Operatoren ignoriert werden.By default, EF Core will log a warning when include operators are ignored. Weitere Informationen zum Anzeigen von Protokollierungsausgaben finden Sie unter Protokollierung.See Logging for more information on viewing logging output. Sie können das Verhalten, wenn ein Include-Operator ignoriert wird, auslösen oder nichts unternehmen.You can change the behavior when an include operator is ignored to either throw or do nothing. Dies geschieht, wenn die Optionen für Ihren Kontext – normalerweise in DbContext.OnConfiguring oder Startup.cs – bei Verwendung von ASP.NET Core eingerichtet werden.This is done when setting up the options for your context - typically in DbContext.OnConfiguring, or in Startup.cs if you are using ASP.NET Core.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;ConnectRetryCount=0")
        .ConfigureWarnings(warnings => warnings.Throw(CoreEventId.IncludeIgnoredWarning));
}

Explizites LadenExplicit loading

Hinweis

Dieses Feature wurde in EF Core 1.1 eingeführt.This feature was introduced in EF Core 1.1.

Sie können eine Navigationseigenschaft über die API DbContext.Entry(...) explizit laden.You can explicitly load a navigation property via the DbContext.Entry(...) API.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    context.Entry(blog)
        .Collection(b => b.Posts)
        .Load();

    context.Entry(blog)
        .Reference(b => b.Owner)
        .Load();
}

Sie können eine Navigationseigenschaft auch explizit laden, indem Sie eine separate Abfrage ausführen, welche die zugehörigen Entitäten zurückgibt.You can also explicitly load a navigation property by executing a seperate query that returns the related entities. Wenn die Änderungsnachverfolgung aktiviert ist, legt EF Core beim Laden einer Entität die Navigationseigenschaften der neu geladenen Entität automatisch so fest, dass diese auf bereits geladene Entitäten verweisen, und die Navigationseigenschaften bereits geladener Entitäten so, dass diese auf die neu geladene Entität verweisen.If change tracking is enabled, then when loading an entity, EF Core will automatically set the navigation properties of the newly-loaded entitiy to refer to any entities already loaded, and set the navigation properties of the already-loaded entities to refer to the newly-loaded entity.

Sie können auch eine LINQ-Abfrage abrufen, welche die Inhalte einer Navigationseigenschaft darstellt.You can also get a LINQ query that represents the contents of a navigation property.

Dadurch können Sie Aktionen, wie z.B. das Ausführen eines Aggregatoperators, über die zugehörigen Entitäten ausführen, ohne diese in den Speicher laden zu müssen.This allows you to do things such as running an aggregate operator over the related entities without loading them into memory.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    var postCount = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Count();
}

Darüber hinaus können Sie filtern, welche zugehörigen Entitäten in den Speicher geladen werden.You can also filter which related entities are loaded into memory.

using (var context = new BloggingContext())
{
    var blog = context.Blogs
        .Single(b => b.BlogId == 1);

    var goodPosts = context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Where(p => p.Rating > 3)
        .ToList();
}

Lazy LoadingLazy loading

Hinweis

Dieses Feature wurde in EF Core 2.1 eingeführt.This feature was introduced in EF Core 2.1.

Die einfachste Möglichkeit für die Verwendung von Lazy Loading besteht in der Installation des Pakets Microsoft.EntityFrameworkCore.Proxies und der Aktivierung dieses Pakets durch Aufrufen von UseLazyLoadingProxies.The simplest way to use lazy-loading is by installing the Microsoft.EntityFrameworkCore.Proxies package and enabling it with a call to UseLazyLoadingProxies. Zum Beispiel:For example:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseLazyLoadingProxies()
        .UseSqlServer(myConnectionString);

Oder bei Verwendung von AddDbContext:Or when using AddDbContext:

    .AddDbContext<BloggingContext>(
        b => b.UseLazyLoadingProxies()
              .UseSqlServer(myConnectionString));

EF Core aktiviert Lazy Loading anschließend für beliebige überschreibbare Navigationseigenschaften – diese müssen virtual sein und in einer Klasse enthalten sein, aus der sie geerbt werden können.EF Core will then enable lazy-loading for any navigation property that can be overridden--that is, it must be virtual and on a class that can be inherited from. In den folgenden Entitäten wird beispielsweise Lazy Loading für die Navigationseigenschaften Post.Blog und Blog.Posts durchgeführt.For example, in the following entities, the Post.Blog and Blog.Posts navigation properties will be lazy-loaded.

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

    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public virtual Blog Blog { get; set; }
}

Lazy Loading ohne ProxysLazy-loading without proxies

Lazy Loading-Proxys funktionieren, indem der Dienst ILazyLoader in eine Entität eingefügt wird, wie unter Entitätstypenkonstruktoren beschrieben.Lazy-loading proxies work by injecting the ILazyLoader service into an entity, as described in Entity Type Constructors. Zum Beispiel:For example:

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

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

    public ICollection<Post> Posts
    {
        get => LazyLoader?.Load(this, ref _posts);
        set => _posts = value;
    }
}

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(ILazyLoader lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private ILazyLoader LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader?.Load(this, ref _blog);
        set => _blog = value;
    }
}

Hierfür ist nicht erforderlich, dass aus Entitätstypen geerbt werden kann oder dass Navigationseigenschaften auf „virtual“ festgelegt sind. Es wird zugelassen, dass Lazy Loading für mit new erstellten Entitätsinstanzen durchgeführt wird, sobald diese an einen Kontext angefügt wurden.This doesn't require entity types to be inherited from or navigation properties to be virtual and allows entity instances created with new to lazy-load once attached to a context. Allerdings muss auf den Dienst ILazyLoader verwiesen werden, der Entitätstypen mit der EF Core-Assembly verbindet.However, it requires a reference to the ILazyLoader service, which couples entity types to the EF Core assembly. Dies kann vermieden werden, indem EF Core zulässt, dass die Methode ILazyLoader.Load als Delegat eingefügt wird.To avoid this EF Core allows the ILazyLoader.Load method to be injected as a delegate. Zum Beispiel:For example:

public class Blog
{
    private ICollection<Post> _posts;

    public Blog()
    {
    }

    private Blog(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

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

    public ICollection<Post> Posts
    {
        get => LazyLoader?.Load(this, ref _posts);
        set => _posts = value;
    }
}

public class Post
{
    private Blog _blog;

    public Post()
    {
    }

    private Post(Action<object, string> lazyLoader)
    {
        LazyLoader = lazyLoader;
    }

    private Action<object, string> LazyLoader { get; set; }

    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog
    {
        get => LazyLoader?.Load(this, ref _blog);
        set => _blog = value;
    }
}

Der oben stehende Code verwendet eine Load-Erweiterungsmethode, um die Verwendung des Delegaten sauberer zu gestalten:The code above uses a Load extension method to make using the delegate a bit cleaner:

public static class PocoLoadingExtensions
{
    public static TRelated Load<TRelated>(
        this Action<object, string> loader,
        object entity,
        ref TRelated navigationField,
        [CallerMemberName] string navigationName = null)
        where TRelated : class
    {
        loader?.Invoke(entity, navigationName);

        return navigationField;
    }
}

Hinweis

Der Konstruktorparameter für den Lazy Loading-Delegaten muss als „lazyLoader“ bezeichnet werden.The constructor parameter for the lazy-loading delegate must be called "lazyLoader". Eine Konfiguration für die Verwendung eines anderen Namens ist für ein zukünftiges Release geplant.Configuration to use a different name this is planned for a future release.

Da EF Core Navigationseigenschaften automatisch korrigiert, können sich in Ihrem Objektgraph Zyklen ergeben.Because EF Core will automatically fix-up navigation properties, you can end up with cycles in your object graph. Das Laden eines Blogs und der zugehörigen Beiträge führt beispielsweise zu einem Blogobjekt, das auf eine Sammlung von Beiträgen verweist.For example, Loading a blog and it's related posts will result in a blog object that references a collection of posts. Die einzelnen Beiträge verweisen wieder auf den Blog.Each of those posts will have a reference back to the blog.

Einige Serialisierungsframeworks lassen solche Zyklen nicht zu.Some serialization frameworks do not allow such cycles. Json.NET löst beispielsweise folgende Ausnahme aus, wenn ein Zyklus erkannt wird.For example, Json.NET will throw the following exception if a cycle is encountered.

Newtonsoft.Json.JsonSerializationException: Für die Eigenschaft „Blog“ mit dem Typ „MyApplication.Models.Blog“ wurde eine auf sich selbst verweisende Schleife erkannt.Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Blog' with type 'MyApplication.Models.Blog'.

Wenn Sie ASP.NET Core verwenden, können Sie Json.NET so konfigurieren, dass im Objektgraph gefundene Zyklen ignoriert werden.If you are using ASP.NET Core, you can configure Json.NET to ignore cycles that it finds in the object graph. Dies erfolgt in der Methode ConfigureServices(...) in Startup.cs.This is done in the ConfigureServices(...) method in Startup.cs.

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddMvc()
        .AddJsonOptions(
            options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
        );

    ...
}