Ładowanie powiązanych danychLoading Related Data

Entity Framework Core umożliwia ładowanie powiązanych jednostek przy użyciu właściwości nawigacji w modelu.Entity Framework Core allows you to use the navigation properties in your model to load related entities. Istnieją trzy powszechnie używane wzorce Obiektowo używana do ładowania powiązane dane.There are three common O/RM patterns used to load related data.

  • Wczesne ładowanie oznacza, że powiązane dane są ładowane z bazy danych w ramach początkowego zapytania.Eager loading means that the related data is loaded from the database as part of the initial query.
  • Jawne ładowanie oznacza, że powiązanych danych jest jawnie załadowane z bazy danych w późniejszym czasie.Explicit loading means that the related data is explicitly loaded from the database at a later time.
  • Powolne ładowanie oznacza, że powiązane przezroczyste załadowaniu danych z bazy danych podczas uzyskiwania dostępu do właściwości nawigacji.Lazy loading means that the related data is transparently loaded from the database when the navigation property is accessed.

Porada

Można wyświetlić w tym artykule przykładowe w witrynie GitHub.You can view this article's sample on GitHub.

Wczesne ładowanieEager loading

Możesz użyć Include metodę, aby określić powiązanych danych, które mają zostać uwzględnione w wynikach kwerendy.You can use the Include method to specify related data to be included in query results. W poniższym przykładzie, blogów, które są zwracane w wynikach będzie mieć ich Posts właściwość wypełniony pokrewnych wpisów.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();
}

Porada

Entity Framework Core będą automatycznie konfigurowania właściwości nawigacji do innych jednostek, które wcześniej zostały załadowane do wystąpienia kontekstu.Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. Dlatego nawet wtedy, gdy jawnie nie zawiera danych dla właściwości nawigacji, nadal można wypełnić właściwość, jeśli niektóre lub wszystkie powiązane jednostki zostały wcześniej załadowane.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.

Może zawierać dane związane z wieloma relacjami w ramach pojedynczego zapytania.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();
}

W tym wiele poziomówIncluding multiple levels

Możesz przejść do szczegółów w relacji do uwzględnienia na wielu poziomach powiązanych danych przy użyciu ThenInclude metody.You can drill down through relationships to include multiple levels of related data using the ThenInclude method. Poniższy przykład ładuje wszystkie blogi, ich pokrewnych wpisów i autor każdego wpisu.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();
}

Uwaga

Bieżące wersje programu Visual Studio oferuje opcje uzupełniania niepoprawny kod i może spowodować, że poprawne wyrażenia do oflagowania z błędami składni, korzystając z ThenInclude metody właściwości nawigacji kolekcji.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. Jest to objaw błędów funkcji IntelliSense w https://github.com/dotnet/roslyn/issues/8237.This is a symptom of an IntelliSense bug tracked at https://github.com/dotnet/roslyn/issues/8237. Jest bezpiecznie zignorować te błędy składniowe fałszywe, tak długo, jak kod jest poprawny i mogą być pomyślnie skompilowane.It is safe to ignore these spurious syntax errors as long as the code is correct and can be compiled successfully.

Można połączyć w łańcuch wielu wywołań ThenInclude aby kontynuować, włączając dalsze poziomy powiązane dane.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();
}

Możesz połączyć wszystkie te które mają zostać objęte powiązane dane z wielu poziomów i wielu katalogów głównych tego samego zapytania.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();
}

Możesz uwzględnić wiele powiązanych jednostek dla jednej jednostki, które jest uwzględniane.You may want to include multiple related entities for one of the entities that is being included. Na przykład podczas wykonywania zapytań dotyczących Blogs, obejmują Posts , a następnie oba Author i Tags z Posts.For example, when querying Blogs, you include Posts and then want to include both the Author and Tags of the Posts. Aby to zrobić, należy określić każdy obejmować ścieżkę, począwszy od głównego.To do this, you need to specify each include path starting at the root. Na przykład Blog -> Posts -> Author i Blog -> Posts -> Tags.For example, Blog -> Posts -> Author and Blog -> Posts -> Tags. Oznacza to, zostanie nadmiarowe sprzężenia, w większości przypadków będzie konsolidować EF sprzężeń podczas generowania 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();
}

Obejmują na typy pochodneInclude on derived types

Może zawierać dane związane z tego zdefiniowany tylko w typie pochodnym przy użyciu Include i ThenInclude.You can include related data from navigations defined only on a derived type using Include and ThenInclude.

Biorąc pod uwagę następujący wzór: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; }
}

Zawartość School nawigacji wszystkie osoby, których uczniowie mogą być eagerly ładowane, przy użyciu wielu wzorców:Contents of School navigation of all People who are Students can be eagerly loaded using a number of patterns:

  • Używanie castusing cast

    context.People.Include(person => ((Student)person).School).ToList()
    
  • za pomocą as — operatorusing as operator

    context.People.Include(person => (person as Student).School).ToList()
    
  • za pomocą przeciążenia Include przyjmującą parametr typu stringusing overload of Include that takes parameter of type string

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

Ignorowane obejmujeIgnored includes

Jeśli zmienisz zapytanie tak, aby nie zwraca wystąpienia typu jednostki, który rozpoczął się zapytania, operatory dołączania są ignorowane.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.

W poniższym przykładzie operatory dołączania są oparte na Blog, ale następnie Select operator jest używany do zmiany zwracanego typu anonimowego przez zapytanie.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. W tym przypadku operatory include nie mają wpływu.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();
}

Domyślnie EF Core zarejestruje ostrzeżenie obejmują gdy operatory są ignorowane.By default, EF Core will log a warning when include operators are ignored. Zobacz rejestrowania więcej informacji na temat wyświetlania danych wyjściowych rejestrowania.See Logging for more information on viewing logging output. Można zmienić zachowanie, gdy include operator jest ignorowana, throw lub nic nie rób.You can change the behavior when an include operator is ignored to either throw or do nothing. Odbywa się podczas konfigurowania opcji kontekstu — zwykle w DbContext.OnConfiguring, lub Startup.cs korzystania z platformy ASP.NET Core.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));
}

Jawne ładowanieExplicit loading

Uwaga

Ta funkcja została wprowadzona w programie EF Core 1.1.This feature was introduced in EF Core 1.1.

Można jawnie załadować właściwości nawigacji za pomocą DbContext.Entry(...) interfejsu API.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();
}

Właściwość nawigacji można także jawnie załadować, wykonując oddzielne zapytania, które zwraca powiązanych jednostek.You can also explicitly load a navigation property by executing a separate query that returns the related entities. Jeśli jest włączone śledzenie zmian, a następnie podczas ładowania jednostki, EF Core będą automatycznie ustawić właściwości nawigacji między jednostkami nowo załadowane do odwoływania się do żadnych jednostek już załadowany i ustawić właściwości nawigacji jednostki już załadowane do odwoływania się do Jednostka nowo załadowane.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.

Można również uzyskać kwerenda LINQ, która reprezentuje zawartość właściwości nawigacji.You can also get a LINQ query that represents the contents of a navigation property.

Dzięki temu można korzystać z możliwości, takie jak uruchomienie aggregate-operator za pośrednictwem powiązanych jednostek bez ładowania ich do pamięci.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();
}

Można również filtrować, które powiązanych jednostek są ładowane do pamięci.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();
}

Ładowanie z opóźnieniemLazy loading

Uwaga

Ta funkcja została wprowadzona w programie EF Core 2.1.This feature was introduced in EF Core 2.1.

Najprostszym sposobem użycia ładowanych z opóźnieniem jest po zainstalowaniu Microsoft.EntityFrameworkCore.Proxies pakietu i włączanie go z wywołaniem UseLazyLoadingProxies.The simplest way to use lazy-loading is by installing the Microsoft.EntityFrameworkCore.Proxies package and enabling it with a call to UseLazyLoadingProxies. Na przykład:For example:

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

Lub w przypadku używania AddDbContext:Or when using AddDbContext:

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

EF Core będzie z opóźnieniem, ładowania dla dowolnej właściwości nawigacji, która może zostać przesłonięta — oznacza to, Włącz musi być virtual i klasy, które mogą być dziedziczone z.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. Na przykład w następujących elementach Post.Blog i Blog.Posts właściwości nawigacji będą ładowane z opóźnieniem.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; }
}

Powolne ładowanie bez serwerów proxyLazy loading without proxies

Serwery proxy ładowanych z opóźnieniem pracy przez iniekcję ILazyLoader usługi do jednostki, zgodnie z opisem w Konstruktorzy typów jednostek.Lazy-loading proxies work by injecting the ILazyLoader service into an entity, as described in Entity Type Constructors. Na przykład: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;
    }
}

To nie wymaga typów jednostek, dziedziczenie lub właściwości nawigacji, aby być wirtualne oraz umożliwia wystąpień jednostek utworzonych za pomocą new z opóźnieniem obciążenia raz przyłączonych do kontekstu.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. Jednak wymaga to dodania odwołania do ILazyLoader usługa, która jest zdefiniowana w Microsoft.EntityFrameworkCore.Abstractions pakietu.However, it requires a reference to the ILazyLoader service, which is defined in the Microsoft.EntityFrameworkCore.Abstractions package. Ten pakiet zawiera minimalny zestaw typów, dzięki czemu istnieje niewielki wpływ w zależności od jego.This package contains a minimal set of types so that there is very little impact in depending on it. Jednak aby całkowicie uniknąć zależności od tego, wszystkie pakiety programu EF Core w typów jednostek, istnieje możliwość wstawić ILazyLoader.Load metody jako pełnomocnik.However, to completely avoid depending on any EF Core packages in the entity types, it is possible to inject the ILazyLoader.Load method as a delegate. Na przykład: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;
    }
}

Kod powyżej używa Load metodę rozszerzenia, aby wprowadzić nieco oczyszczarkę plików przy użyciu delegata: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;
    }
}

Uwaga

Parametr konstruktora delegata ładowanych z opóźnieniem należy wywołać "lazyLoader".The constructor parameter for the lazy-loading delegate must be called "lazyLoader". Konfigurację, aby użyć innej nazwy, niż jest to planowana w przyszłej wersji.Configuration to use a different name than this is planned for a future release.

Ponieważ właściwości nawigacji automatycznie poprawki będą programu EF Core, można na końcu cykli w grafie obiektu.Because EF Core will automatically fix-up navigation properties, you can end up with cycles in your object graph. Na przykład blog i jej powiązane wpisy ładowania spowoduje obiekt blogu, który odwołuje się zbiór wpisów.For example, loading a blog and its related posts will result in a blog object that references a collection of posts. Każda z tych wpisów mają odwołaniem zwrotnym do blogu.Each of those posts will have a reference back to the blog.

Niektóre środowiska serializacji nie zezwalają na tych cykli.Some serialization frameworks do not allow such cycles. Na przykład na składnik Json.NET zgłosi następujący wyjątek, jeśli okaże się cyklu.For example, Json.NET will throw the following exception if a cycle is encountered.

Newtonsoft.Json.JsonSerializationException: Self odwołujące się do pętli wykryto dla właściwości "Blog" z typem "MyApplication.Models.Blog".Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Blog' with type 'MyApplication.Models.Blog'.

Jeśli używasz platformy ASP.NET Core, można skonfigurować program Json.NET, aby zignorować cykle, które znajdzie się na grafie obiektu.If you are using ASP.NET Core, you can configure Json.NET to ignore cycles that it finds in the object graph. Jest to realizowane w ConfigureServices(...) method in Class metoda 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
        );

    ...
}