Udostępnij za pośrednictwem


Uzyskiwanie dostępu do śledzonych jednostek

Istnieją cztery główne interfejsy API służące do uzyskiwania dostępu do jednostek śledzonych przez element DbContext:

Każdy z nich został opisany bardziej szczegółowo w poniższych sekcjach.

Napiwek

W tym dokumencie przyjęto założenie, że stany jednostki i podstawy śledzenia zmian platformy EF Core są zrozumiałe. Aby uzyskać więcej informacji na temat tych tematów, zobacz Change Tracking in EF Core (Śledzenie zmian w programie EF Core ).

Napiwek

Możesz uruchomić i debugować cały kod podany w tym dokumencie, pobierając przykładowy kod z serwisu GitHub.

Używanie wystąpień DbContext.Entry i EntityEntry

Dla każdej śledzonej jednostki program Entity Framework Core (EF Core) śledzi następujące elementy:

  • Ogólny stan jednostki. Jest to jeden z Unchangedelementów , Modified, Addedlub Deleted; zobacz Change Tracking in EF Core (Śledzenie zmian w programie EF Core ), aby uzyskać więcej informacji.
  • Relacje między śledzonych jednostek. Na przykład blog, do którego należy wpis.
  • "bieżące wartości" właściwości.
  • "oryginalne wartości" właściwości, gdy te informacje są dostępne. Oryginalne wartości to wartości właściwości, które istniały podczas wykonywania zapytań o jednostkę z bazy danych.
  • Które wartości właściwości zostały zmodyfikowane od czasu ich odpyt.
  • Inne informacje o wartościach właściwości, takie jak to, czy wartość jest tymczasowa.

Przekazanie wystąpienia jednostki w celu DbContext.Entry uzyskania EntityEntry<TEntity> dostępu do tych informacji dla danej jednostki. Przykład:

using var context = new BlogsContext();

var blog = context.Blogs.Single(e => e.Id == 1);
var entityEntry = context.Entry(blog);

W poniższych sekcjach pokazano, jak używać jednostki EntityEntry do uzyskiwania dostępu do stanu jednostki i manipulowania nim, a także stanu właściwości i nawigacji jednostki.

Praca z jednostką

Najczęstszym zastosowaniem programu EntityEntry<TEntity> jest uzyskanie dostępu do bieżącej EntityState jednostki. Przykład:

var currentState = context.Entry(blog).State;
if (currentState == EntityState.Unchanged)
{
    context.Entry(blog).State = EntityState.Modified;
}

Metoda Entry może być również używana w jednostkach, które nie są jeszcze śledzone. Nie rozpoczyna to śledzenia jednostki; stan jednostki to nadal Detached. Jednak zwrócona jednostka EntityEntry może następnie służyć do zmiany stanu jednostki, w którym to momencie jednostka zostanie śledzona w danym stanie. Na przykład następujący kod rozpocznie śledzenie wystąpienia bloga jako Added:

var newBlog = new Blog();
Debug.Assert(context.Entry(newBlog).State == EntityState.Detached);

context.Entry(newBlog).State = EntityState.Added;
Debug.Assert(context.Entry(newBlog).State == EntityState.Added);

Napiwek

W przeciwieństwie do platformy EF6 ustawienie stanu pojedynczej jednostki nie spowoduje śledzenia wszystkich połączonych jednostek. Dzięki temu ustawienie stanu w ten sposób jest operacją niższego poziomu niż wywoływanie Addoperacji , Attachlub Update, które działają na całym grafie jednostek.

W poniższej tabeli przedstawiono podsumowanie sposobów użycia jednostki EntityEntry do pracy z całą jednostką:

Element członkowski EntityEntry opis
EntityEntry.State Pobiera i ustawia EntityState jednostkę.
EntityEntry.Entity Pobiera wystąpienie jednostki.
EntityEntry.Context Element DbContext śledzący tę jednostkę.
EntityEntry.Metadata IEntityType metadanych typu jednostki.
EntityEntry.IsKeySet Określa, czy jednostka ma ustawioną wartość klucza.
EntityEntry.Reload() Zastępuje wartości właściwości wartościami odczytanymi z bazy danych.
EntityEntry.DetectChanges() Wymusza wykrywanie zmian tylko dla tej jednostki; zobacz Wykrywanie zmian i powiadomienia.

Praca z jedną właściwością

Kilka przeciążeń zezwalania EntityEntry<TEntity>.Property na dostęp do informacji o poszczególnych właściwościach jednostki. Na przykład przy użyciu silnie typizowanego, płynnego interfejsu API:

PropertyEntry<Blog, string> propertyEntry = context.Entry(blog).Property(e => e.Name);

Zamiast tego nazwę właściwości można przekazać jako ciąg. Przykład:

PropertyEntry<Blog, string> propertyEntry = context.Entry(blog).Property<string>("Name");

Zwrócony PropertyEntry<TEntity,TProperty> element może następnie służyć do uzyskiwania dostępu do informacji o właściwości. Na przykład może służyć do pobierania i ustawiania bieżącej wartości właściwości w tej jednostce:

string currentValue = context.Entry(blog).Property(e => e.Name).CurrentValue;
context.Entry(blog).Property(e => e.Name).CurrentValue = "1unicorn2";

Obie metody właściwości użyte powyżej zwracają silnie typizowane wystąpienie ogólne PropertyEntry<TEntity,TProperty> . Użycie tego typu ogólnego jest preferowane, ponieważ umożliwia dostęp do wartości właściwości bez typów wartości pól. Jeśli jednak typ jednostki lub właściwości nie jest znany w czasie kompilacji, można uzyskać zamiast tego nieogólne PropertyEntry :

PropertyEntry propertyEntry = context.Entry(blog).Property("Name");

Umożliwia to dostęp do informacji o właściwości dla dowolnej właściwości niezależnie od typu, kosztem typów wartości boxingu. Przykład:

object blog = context.Blogs.Single(e => e.Id == 1);

object currentValue = context.Entry(blog).Property("Name").CurrentValue;
context.Entry(blog).Property("Name").CurrentValue = "1unicorn2";

Poniższa tabela zawiera podsumowanie informacji o właściwości uwidocznionych przez właściwośćEntry:

Element członkowski PropertyEntry opis
PropertyEntry<TEntity,TProperty>.CurrentValue Pobiera i ustawia bieżącą wartość właściwości.
PropertyEntry<TEntity,TProperty>.OriginalValue Pobiera i ustawia oryginalną wartość właściwości, jeśli jest dostępna.
PropertyEntry<TEntity,TProperty>.EntityEntry Odwołanie wsteczne EntityEntry<TEntity> do elementu dla jednostki.
PropertyEntry.Metadata IProperty metadane właściwości .
PropertyEntry.IsModified Wskazuje, czy ta właściwość jest oznaczona jako zmodyfikowana i umożliwia zmianę tego stanu.
PropertyEntry.IsTemporary Wskazuje, czy ta właściwość jest oznaczona jako tymczasowa i umożliwia zmianę tego stanu.

Uwagi:

  • Oryginalna wartość właściwości to wartość, którą właściwość miała podczas wykonywania zapytania o jednostkę z bazy danych. Jednak oryginalne wartości nie są dostępne, jeśli jednostka została odłączona, a następnie jawnie dołączona do innej klasy DbContext, na przykład z elementem Attach lub Update. W takim przypadku zwrócona oryginalna wartość będzie taka sama jak bieżąca wartość.
  • SaveChanges Zaktualizuje tylko właściwości oznaczone jako zmodyfikowane. Ustaw IsModified wartość true, aby wymusić aktualizację danej wartości właściwości przez program EF Core lub ustawić ją na wartość false, aby zapobiec aktualizowaniu wartości właściwości przez program EF Core.
  • Wartości tymczasowe są zwykle generowane przez generatory wartości platformy EF Core. Ustawienie bieżącej wartości właściwości spowoduje zastąpienie wartości tymczasowej daną wartością i oznaczenie właściwości jako nie tymczasowej. Ustaw IsTemporary wartość true, aby wymusić tymczasowe ustawienie wartości nawet po jej jawnym ustawieniu.

Praca z pojedynczą nawigacją

Kilka przeciążeń elementów EntityEntry<TEntity>.Reference, EntityEntry<TEntity>.Collectioni EntityEntry.Navigation umożliwia dostęp do informacji o poszczególnych nawigacji.

Dostęp do nawigacji odwołań do pojedynczej powiązanej jednostki jest uzyskiwany za pośrednictwem Reference metod. Nawigacja referencyjna wskazuje strony "jeden" relacji jeden-do-wielu i obie strony relacji jeden do jednego. Przykład:

ReferenceEntry<Post, Blog> referenceEntry1 = context.Entry(post).Reference(e => e.Blog);
ReferenceEntry<Post, Blog> referenceEntry2 = context.Entry(post).Reference<Blog>("Blog");
ReferenceEntry referenceEntry3 = context.Entry(post).Reference("Blog");

Nawigacje mogą być również kolekcjami powiązanych jednostek, gdy są używane dla relacji "wiele do wielu" i relacji wiele-do-wielu. Metody Collection są używane do uzyskiwania dostępu do nawigacji kolekcji. Przykład:

CollectionEntry<Blog, Post> collectionEntry1 = context.Entry(blog).Collection(e => e.Posts);
CollectionEntry<Blog, Post> collectionEntry2 = context.Entry(blog).Collection<Post>("Posts");
CollectionEntry collectionEntry3 = context.Entry(blog).Collection("Posts");

Niektóre operacje są wspólne dla wszystkich nawigacji. Dostęp do nich można uzyskać zarówno w przypadku nawigacji odwołań, jak i kolekcji przy użyciu EntityEntry.Navigation metody . Należy pamiętać, że dostęp do wszystkich nawigacji jest dostępny tylko wtedy, gdy dostęp do wszystkich nawigacji jest dostępny. Przykład:

NavigationEntry navigationEntry = context.Entry(blog).Navigation("Posts");

W poniższej tabeli przedstawiono podsumowanie sposobów używania elementów ReferenceEntry<TEntity,TProperty>, CollectionEntry<TEntity,TRelatedEntity>i NavigationEntry:

Element członkowski NavigationEntry opis
MemberEntry.CurrentValue Pobiera i ustawia bieżącą wartość nawigacji. Jest to cała kolekcja nawigacji kolekcji.
NavigationEntry.Metadata INavigationBase metadane nawigacji.
NavigationEntry.IsLoaded Pobiera lub ustawia wartość wskazującą, czy powiązana jednostka lub kolekcja została w pełni załadowana z bazy danych.
NavigationEntry.Load() Ładuje powiązaną jednostkę lub kolekcję z bazy danych; zobacz Jawne ładowanie powiązanych danych.
NavigationEntry.Query() Zapytanie EF Core służy do ładowania tej nawigacji jako elementu IQueryable , który może być bardziej złożony. Zobacz Jawne ładowanie powiązanych danych.

Praca ze wszystkimi właściwościami jednostki

EntityEntry.Properties zwraca wartość dla IEnumerable<T> PropertyEntry każdej właściwości jednostki. Może to służyć do wykonywania akcji dla każdej właściwości jednostki. Aby na przykład ustawić dowolną właściwość DateTime na :DateTime.Now

foreach (var propertyEntry in context.Entry(blog).Properties)
{
    if (propertyEntry.Metadata.ClrType == typeof(DateTime))
    {
        propertyEntry.CurrentValue = DateTime.Now;
    }
}

Ponadto entityEntry zawiera kilka metod pobierania i ustawiania wszystkich wartości właściwości w tym samym czasie. Te metody używają PropertyValues klasy, która reprezentuje kolekcję właściwości i ich wartości. WłaściwościValue można uzyskać dla bieżących lub oryginalnych wartości lub wartości, które są obecnie przechowywane w bazie danych. Przykład:

var currentValues = context.Entry(blog).CurrentValues;
var originalValues = context.Entry(blog).OriginalValues;
var databaseValues = context.Entry(blog).GetDatabaseValues();

Te obiekty PropertyValues nie są bardzo przydatne samodzielnie. Można je jednak połączyć w celu wykonywania typowych operacji potrzebnych podczas manipulowania jednostkami. Jest to przydatne podczas pracy z obiektami transferu danych i rozwiązywania optymistycznych konfliktów współbieżności. W poniższych sekcjach przedstawiono kilka przykładów.

Ustawianie bieżących lub oryginalnych wartości z jednostki lub obiektu DTO

Bieżące lub oryginalne wartości jednostki można zaktualizować, kopiując wartości z innego obiektu. Rozważmy na przykład BlogDto obiekt transferu danych (DTO) z tymi samymi właściwościami co typ jednostki:

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

Może to służyć do ustawiania bieżących wartości śledzonej jednostki przy użyciu polecenia PropertyValues.SetValues:

var blogDto = new BlogDto { Id = 1, Name = "1unicorn2" };

context.Entry(blog).CurrentValues.SetValues(blogDto);

Ta technika jest czasami używana podczas aktualizowania jednostki przy użyciu wartości uzyskanych z wywołania usługi lub klienta w aplikacji n-warstwowej. Należy pamiętać, że użyty obiekt nie musi być tego samego typu co jednostka, o ile ma właściwości, których nazwy pasują do tych z jednostki. W powyższym przykładzie wystąpienie obiektu DTO BlogDto służy do ustawiania bieżących wartości śledzonej Blog jednostki.

Należy pamiętać, że właściwości zostaną oznaczone jako zmodyfikowane tylko wtedy, gdy zestaw wartości różni się od bieżącej wartości.

Ustawianie bieżących lub oryginalnych wartości ze słownika

W poprzednim przykładzie ustawiono wartości z jednostki lub wystąpienia DTO. To samo zachowanie jest dostępne, gdy wartości właściwości są przechowywane jako pary nazwa/wartość w słowniku. Przykład:

var blogDictionary = new Dictionary<string, object> { ["Id"] = 1, ["Name"] = "1unicorn2" };

context.Entry(blog).CurrentValues.SetValues(blogDictionary);

Ustawianie bieżących lub oryginalnych wartości z bazy danych

Bieżące lub oryginalne wartości jednostki można zaktualizować przy użyciu najnowszych wartości z bazy danych przez wywołanie GetDatabaseValues() lub GetDatabaseValuesAsync użycie zwróconego obiektu w celu ustawienia bieżących lub oryginalnych wartości albo obu tych wartości. Przykład:

var databaseValues = context.Entry(blog).GetDatabaseValues();
context.Entry(blog).CurrentValues.SetValues(databaseValues);
context.Entry(blog).OriginalValues.SetValues(databaseValues);

Tworzenie sklonowanego obiektu zawierającego bieżące, oryginalne lub wartości bazy danych

Obiekt PropertyValues zwrócony z wartości CurrentValues, OriginalValues lub GetDatabaseValues może służyć do utworzenia klonu jednostki przy użyciu polecenia PropertyValues.ToObject(). Przykład:

var clonedBlog = context.Entry(blog).GetDatabaseValues().ToObject();

Zwróć uwagę, że ToObject zwraca nowe wystąpienie, które nie jest śledzone przez obiekt DbContext. Zwrócony obiekt nie ma również żadnych relacji ustawionych na inne jednostki.

Sklonowany obiekt może być przydatny do rozwiązywania problemów związanych z współbieżnych aktualizacji bazy danych, zwłaszcza w przypadku powiązania danych z obiektami określonego typu. Aby uzyskać więcej informacji, zobacz optymistyczną współbieżność .

Praca ze wszystkimi nawigacjami jednostki

EntityEntry.Navigations zwraca wartość dla IEnumerable<T> NavigationEntry każdej nawigacji jednostki. EntityEntry.References i EntityEntry.Collections zrobić to samo, ale ograniczone odpowiednio do odwołań lub nawigacji kolekcji. Może to służyć do wykonywania akcji dla każdej nawigacji jednostki. Aby na przykład wymusić ładowanie wszystkich powiązanych jednostek:

foreach (var navigationEntry in context.Entry(blog).Navigations)
{
    navigationEntry.Load();
}

Praca ze wszystkimi członkami jednostki

Zwykłe właściwości i właściwości nawigacji mają inny stan i zachowanie. W związku z tym często przetwarza się nawigacje i nienawigowania oddzielnie, jak pokazano w powyższych sekcjach. Jednak czasami przydatne może być zrobienie czegoś z dowolnym elementem członkowskim jednostki, niezależnie od tego, czy jest to zwykła właściwość, czy nawigacja. EntityEntry.Member i EntityEntry.Members są one udostępniane w tym celu. Przykład:

foreach (var memberEntry in context.Entry(blog).Members)
{
    Console.WriteLine(
        $"Member {memberEntry.Metadata.Name} is of type {memberEntry.Metadata.ClrType.ShortDisplayName()} and has value {memberEntry.CurrentValue}");
}

Uruchomienie tego kodu na blogu z przykładu generuje następujące dane wyjściowe:

Member Id is of type int and has value 1
Member Name is of type string and has value .NET Blog
Member Posts is of type IList<Post> and has value System.Collections.Generic.List`1[Post]

Napiwek

Widok debugowania monitora zmian zawiera informacje podobne do tych. Widok debugowania dla całego monitora zmian jest generowany na podstawie poszczególnych EntityEntry.DebugView śledzonych jednostek.

Znajdowanie i znajdowanie asynchroniczne

DbContext.Find, , DbContext.FindAsyncDbSet<TEntity>.Findi DbSet<TEntity>.FindAsync są przeznaczone do wydajnego wyszukiwania pojedynczej jednostki, gdy jest znany jego klucz podstawowy. Znajdź pierwsze sprawdzanie, czy jednostka jest już śledzona, a jeśli tak, zwraca jednostkę natychmiast. Zapytanie bazy danych jest wykonywane tylko wtedy, gdy jednostka nie jest śledzona lokalnie. Rozważmy na przykład ten kod, który wywołuje metodę Find dwa razy dla tej samej jednostki:

using var context = new BlogsContext();

Console.WriteLine("First call to Find...");
var blog1 = context.Blogs.Find(1);

Console.WriteLine($"...found blog {blog1.Name}");

Console.WriteLine();
Console.WriteLine("Second call to Find...");
var blog2 = context.Blogs.Find(1);
Debug.Assert(blog1 == blog2);

Console.WriteLine("...returned the same instance without executing a query.");

Dane wyjściowe z tego kodu (w tym rejestrowanie programu EF Core) w przypadku korzystania z biblioteki SQLite to:

First call to Find...
info: 12/29/2020 07:45:53.682 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (1ms) [Parameters=[@__p_0='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
      SELECT "b"."Id", "b"."Name"
      FROM "Blogs" AS "b"
      WHERE "b"."Id" = @__p_0
      LIMIT 1
...found blog .NET Blog

Second call to Find...
...returned the same instance without executing a query.

Zwróć uwagę, że pierwsze wywołanie nie znajduje jednostki lokalnie, dlatego wykonuje zapytanie bazy danych. Z drugiej strony drugie wywołanie zwraca to samo wystąpienie bez wykonywania zapytań względem bazy danych, ponieważ jest już śledzone.

Funkcja Find zwraca wartość null, jeśli jednostka z danym kluczem nie jest śledzona lokalnie i nie istnieje w bazie danych.

Klucze złożone

Funkcja Znajdź może być również używana z kluczami złożonymi. Rozważmy na przykład OrderLine jednostkę z kluczem złożonym składającym się z identyfikatora zamówienia i identyfikatora produktu:

public class OrderLine
{
    public int OrderId { get; set; }
    public int ProductId { get; set; }

    //...
}

Klucz złożony musi być skonfigurowany, DbContext.OnModelCreating aby zdefiniować kluczowe części i ich kolejność. Przykład:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<OrderLine>()
        .HasKey(e => new { e.OrderId, e.ProductId });
}

Zwróć uwagę, że OrderId jest to pierwsza część klucza i ProductId jest drugą częścią klucza. Ta kolejność musi być używana podczas przekazywania wartości kluczy do funkcji Znajdź. Przykład:

var orderline = context.OrderLines.Find(orderId, productId);

Uzyskiwanie dostępu do wszystkich śledzonych jednostek przy użyciu biblioteki ChangeTracker.Entries

Do tej pory mieliśmy dostęp tylko do jednego EntityEntry naraz. ChangeTracker.Entries() Zwraca element EntityEntry dla każdej jednostki aktualnie śledzonej przez obiekt DbContext. Przykład:

using var context = new BlogsContext();
var blogs = context.Blogs.Include(e => e.Posts).ToList();

foreach (var entityEntry in context.ChangeTracker.Entries())
{
    Console.WriteLine($"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property("Id").CurrentValue}");
}

Ten kod generuje następujące dane wyjściowe:

Found Blog entity with ID 1
Found Post entity with ID 1
Found Post entity with ID 2

Zwróć uwagę, że zwracane są wpisy zarówno dla blogów, jak i wpisów. Zamiast tego wyniki można filtrować do określonego ChangeTracker.Entries<TEntity>() typu jednostki przy użyciu przeciążenia ogólnego:

foreach (var entityEntry in context.ChangeTracker.Entries<Post>())
{
    Console.WriteLine(
        $"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property(e => e.Id).CurrentValue}");
}

Dane wyjściowe z tego kodu pokazują, że zwracane są tylko wpisy:

Found Post entity with ID 1
Found Post entity with ID 2

Ponadto użycie przeciążenia ogólnego zwraca wystąpienia ogólne EntityEntry<TEntity> . W tym przykładzie umożliwia to płynne uzyskiwanie dostępu do Id właściwości.

Typ ogólny używany do filtrowania nie musi być mapowanym typem jednostki; Zamiast tego można użyć niezamapowanego typu podstawowego lub interfejsu. Jeśli na przykład wszystkie typy jednostek w modelu implementują interfejs definiujący ich właściwość klucza:

public interface IEntityWithKey
{
    int Id { get; set; }
}

Następnie ten interfejs może służyć do pracy z kluczem dowolnej śledzonej jednostki w sposób silnie typizowane. Przykład:

foreach (var entityEntry in context.ChangeTracker.Entries<IEntityWithKey>())
{
    Console.WriteLine(
        $"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property(e => e.Id).CurrentValue}");
}

Używanie elementu DbSet.Local do wykonywania zapytań o śledzone jednostki

Zapytania ef Core są zawsze wykonywane w bazie danych i zwracają tylko jednostki, które zostały zapisane w bazie danych. DbSet<TEntity>.Local Udostępnia mechanizm wysyłania zapytań do elementu DbContext dla jednostek lokalnych, śledzonych.

Ponieważ DbSet.Local służy do wykonywania zapytań dotyczących śledzonych jednostek, typowe jest ładowanie jednostek do elementu DbContext, a następnie praca z tymi załadowanymi jednostkami. Dotyczy to szczególnie powiązania danych, ale może być również przydatne w innych sytuacjach. Na przykład w poniższym kodzie baza danych jest najpierw odpytywane dla wszystkich blogów i wpisów. Metoda Load rozszerzenia służy do wykonywania tego zapytania z wynikami śledzonym przez kontekst bez zwracania bezpośrednio do aplikacji. (Użycie ToList lub podobne ma taki sam efekt, ale z obciążeniem tworzenia zwracanej listy, która nie jest potrzebna tutaj). W tym przykładzie użyto DbSet.Local metody w celu uzyskania dostępu do lokalnie śledzonych jednostek:

using var context = new BlogsContext();

context.Blogs.Include(e => e.Posts).Load();

foreach (var blog in context.Blogs.Local)
{
    Console.WriteLine($"Blog: {blog.Name}");
}

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"Post: {post.Title}");
}

Zwróć uwagę, że w przeciwieństwie do ChangeTracker.Entries()DbSet.Local klasy funkcja zwraca wystąpienia jednostek bezpośrednio. Jednostka EntityEntry może oczywiście być zawsze uzyskiwana dla zwróconej jednostki przez wywołanie metody DbContext.Entry.

Widok lokalny

DbSet<TEntity>.Local Zwraca widok lokalnie śledzonych jednostek, który odzwierciedla bieżącą część EntityState tych jednostek. W szczególności oznacza to, że:

  • Added jednostki są uwzględniane. Należy pamiętać, że nie jest tak w przypadku zwykłych zapytań platformy EF Core, ponieważ Added jednostki nie istnieją jeszcze w bazie danych, dlatego nigdy nie są zwracane przez zapytanie bazy danych.
  • Deleted jednostki są wykluczone. Należy pamiętać, że nie dotyczy to zwykłych zapytań platformy EF Core, ponieważ Deleted jednostki nadal istnieją w bazie danych i dlatego zwracane przez zapytania bazy danych.

Wszystko to oznacza, że DbSet.Local jest to widok danych, które odzwierciedlają bieżący stan koncepcyjny grafu jednostki, z jednostkami dołączonymi Added i Deleted wykluczonymi jednostkami. Jest to zgodne z oczekiwanym stanem bazy danych po wywołaniu funkcji SaveChanges.

Jest to zazwyczaj idealny widok powiązania danych, ponieważ przedstawia użytkownikowi dane w miarę ich zrozumienia na podstawie zmian wprowadzonych przez aplikację.

Poniższy kod pokazuje to, oznaczając jeden wpis jako Deleted , a następnie dodając nowy wpis, oznaczając go jako Added:

using var context = new BlogsContext();

var posts = context.Posts.Include(e => e.Blog).ToList();

Console.WriteLine("Local view after loading posts:");

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"  Post: {post.Title}");
}

context.Remove(posts[1]);

context.Add(
    new Post
    {
        Title = "What’s next for System.Text.Json?",
        Content = ".NET 5.0 was released recently and has come with many...",
        Blog = posts[0].Blog
    });

Console.WriteLine("Local view after adding and deleting posts:");

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"  Post: {post.Title}");
}

Dane wyjściowe z tego kodu to:

Local view after loading posts:
  Post: Announcing the Release of EF Core 5.0
  Post: Announcing F# 5
  Post: Announcing .NET 5.0
Local view after adding and deleting posts:
  Post: What’s next for System.Text.Json?
  Post: Announcing the Release of EF Core 5.0
  Post: Announcing .NET 5.0

Zwróć uwagę, że usunięty wpis jest usuwany z widoku lokalnego, a dodany wpis jest dołączany.

Dodawanie i usuwanie jednostek przy użyciu ustawień lokalnych

DbSet<TEntity>.Local Zwraca wystąpienie LocalView<TEntity>klasy . Jest to implementacja ICollection<T> , która generuje powiadomienia i reaguje na powiadomienia, gdy jednostki są dodawane i usuwane z kolekcji. (Jest to taka sama koncepcja jak ObservableCollection<T>, ale zaimplementowana jako projekcja w istniejących wpisach śledzenia zmian platformy EF Core, a nie jako niezależnej kolekcji).

Powiadomienia widoku lokalnego są przyłączone do śledzenia zmian DbContext w taki sposób, że widok lokalny pozostaje zsynchronizowany z elementem DbContext. Szczególnie:

  • Dodanie nowej jednostki w celu DbSet.Local powoduje, że będzie ona śledzona przez element DbContext, zazwyczaj w Added stanie . (Jeśli jednostka ma już wygenerowaną wartość klucza, jest ona śledzona jako Unchanged zamiast tego).
  • Usunięcie jednostki z DbSet.Local powoduje oznaczenie jej jako Deleted.
  • Jednostka, która zostanie śledzona przez obiekt DbContext, zostanie automatycznie wyświetlona w kolekcji DbSet.Local . Na przykład wykonanie zapytania w celu automatycznego wprowadzenia większej liczby jednostek powoduje zaktualizowanie widoku lokalnego.
  • Jednostka oznaczona jako Deleted zostanie automatycznie usunięta z kolekcji lokalnej.

Oznacza to, że widok lokalny może służyć do manipulowania śledzonych jednostek po prostu przez dodanie i usunięcie z kolekcji. Zmodyfikujmy na przykład poprzedni przykładowy kod, aby dodać i usunąć wpisy z kolekcji lokalnej:

using var context = new BlogsContext();

var posts = context.Posts.Include(e => e.Blog).ToList();

Console.WriteLine("Local view after loading posts:");

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"  Post: {post.Title}");
}

context.Posts.Local.Remove(posts[1]);

context.Posts.Local.Add(
    new Post
    {
        Title = "What’s next for System.Text.Json?",
        Content = ".NET 5.0 was released recently and has come with many...",
        Blog = posts[0].Blog
    });

Console.WriteLine("Local view after adding and deleting posts:");

foreach (var post in context.Posts.Local)
{
    Console.WriteLine($"  Post: {post.Title}");
}

Dane wyjściowe pozostają niezmienione z poprzedniego przykładu, ponieważ zmiany wprowadzone w widoku lokalnym są synchronizowane z elementem DbContext.

Korzystanie z widoku lokalnego dla powiązań danych windows Forms lub WPF

DbSet<TEntity>.Local stanowi podstawę powiązania danych z jednostkami programu EF Core. Jednak zarówno Formularze systemu Windows, jak i WPF działają najlepiej w przypadku użycia z określonym typem powiadamiania kolekcji, że są one oczekiwane. Widok lokalny obsługuje tworzenie tych określonych typów kolekcji:

Przykład:

ObservableCollection<Post> observableCollection = context.Posts.Local.ToObservableCollection();
BindingList<Post> bindingList = context.Posts.Local.ToBindingList();

Aby uzyskać więcej informacji na temat powiązania danych WPF z programem EF Core, zobacz Rozpoczynanie pracy z platformą Windows Forms , aby uzyskać więcej informacji na temat powiązania danych formularzy systemu Windows z programem EF Core.

Napiwek

Widok lokalny dla danego wystąpienia DbSet jest tworzony leniwie po pierwszym uzyskiwaniu dostępu, a następnie buforowany. Samo tworzenie obiektu LocalView jest szybkie i nie korzysta ze znacznej ilości pamięci. Jednak wywołuje metodę DetectChanges, co może być powolne w przypadku dużej liczby jednostek. Kolekcje utworzone przez ToObservableCollection program i ToBindingList są również tworzone leniwie, a następnie buforowane. Obie te metody tworzą nowe kolekcje, które mogą być powolne i korzystać z dużej ilości pamięci, gdy są zaangażowane tysiące jednostek.