Bagikan melalui


Mengakses Entitas terlacak

Ada empat API utama untuk mengakses entitas yang DbContextdilacak oleh :

Masing-masing dijelaskan secara lebih rinci di bagian di bawah ini.

Tip

Dokumen ini mengasumsikan bahwa status entitas dan dasar-dasar pelacakan perubahan EF Core dipahami. Lihat Pelacakan Perubahan di EF Core untuk informasi selengkapnya tentang topik ini.

Tip

Anda dapat menjalankan dan men-debug ke semua kode dalam dokumen ini dengan mengunduh kode sampel dari GitHub.

Menggunakan instans DbContext.Entry dan EntityEntry

Untuk setiap entitas yang dilacak, Entity Framework Core (EF Core) melacak:

  • Status keseluruhan entitas. Ini adalah salah satu dari Unchanged, , AddedModified, atau Deleted; lihat Pelacakan Perubahan di EF Core untuk informasi selengkapnya.
  • Hubungan antara entitas yang dilacak. Misalnya, blog tempat postingan berada.
  • "Nilai saat ini" properti.
  • "Nilai asli" properti, ketika informasi ini tersedia. Nilai asli adalah nilai properti yang ada saat entitas dikueri dari database.
  • Nilai properti mana yang telah dimodifikasi sejak dikueri.
  • Informasi lain tentang nilai properti, seperti apakah nilai tersebut bersifat sementara atau tidak.

Meneruskan instans entitas untuk DbContext.Entry menghasilkan memberikan EntityEntry<TEntity> akses ke informasi ini untuk entitas tertentu. Contohnya:

using var context = new BlogsContext();

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

Bagian berikut menunjukkan cara menggunakan EntityEntry untuk mengakses dan memanipulasi status entitas, serta status properti dan navigasi entitas.

Bekerja dengan entitas

Penggunaan EntityEntry<TEntity> yang paling umum adalah mengakses arus EntityState entitas. Contohnya:

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

Metode Entri juga dapat digunakan pada entitas yang belum dilacak. Ini tidak mulai melacak entitas; status entitas masih Detached. Namun, EntityEntry yang dikembalikan kemudian dapat digunakan untuk mengubah status entitas, di mana entitas akan dilacak dalam status tertentu. Misalnya, kode berikut akan mulai melacak instans Blog sebagai 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);

Tip

Tidak seperti di EF6, mengatur status entitas individu tidak akan menyebabkan semua entitas yang terhubung dilacak. Ini membuat pengaturan status dengan cara ini operasi tingkat lebih rendah daripada memanggil Add, , Attachatau Update, yang beroperasi pada seluruh grafik entitas.

Tabel berikut ini meringkas cara menggunakan EntityEntry untuk bekerja dengan seluruh entitas:

Anggota EntityEntry Deskripsi
EntityEntry.State Mendapatkan dan mengatur EntityState entitas.
EntityEntry.Entity Mendapatkan instans entitas.
EntityEntry.Context Yang DbContext melacak entitas ini.
EntityEntry.Metadata IEntityType metadata untuk jenis entitas.
EntityEntry.IsKeySet Apakah entitas telah menetapkan nilai kuncinya atau tidak.
EntityEntry.Reload() Menimpa nilai properti dengan nilai yang dibaca dari database.
EntityEntry.DetectChanges() Memaksa deteksi perubahan untuk entitas ini saja; lihat Deteksi Perubahan dan Pemberitahuan.

Bekerja dengan satu properti

Beberapa kelebihan beban EntityEntry<TEntity>.Property izinkan akses ke informasi tentang properti individual entitas. Misalnya, menggunakan API yang sangat ditik, seperti fasih:

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

Nama properti dapat diteruskan sebagai string. Contohnya:

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

Yang dikembalikan PropertyEntry<TEntity,TProperty> kemudian dapat digunakan untuk mengakses informasi tentang properti . Misalnya, dapat digunakan untuk mendapatkan dan mengatur nilai properti saat ini pada entitas ini:

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

Kedua metode Properti yang digunakan di atas mengembalikan instans generik PropertyEntry<TEntity,TProperty> yang sangat ditik. Menggunakan jenis generik ini lebih disukai karena memungkinkan akses ke nilai properti tanpa jenis nilai tinju. Namun, jika jenis entitas atau properti tidak diketahui pada waktu kompilasi, maka non-generik PropertyEntry dapat diperoleh sebagai gantinya:

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

Ini memungkinkan akses ke informasi properti untuk properti apa pun terlepas dari jenisnya, dengan mengorbankan jenis nilai tinju. Contohnya:

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

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

Tabel berikut ini meringkas informasi properti yang diekspos oleh PropertyEntry:

Anggota PropertyEntry Deskripsi
PropertyEntry<TEntity,TProperty>.CurrentValue Mendapatkan dan mengatur nilai properti saat ini.
PropertyEntry<TEntity,TProperty>.OriginalValue Mendapatkan dan mengatur nilai asli properti, jika tersedia.
PropertyEntry<TEntity,TProperty>.EntityEntry Referensi kembali ke EntityEntry<TEntity> untuk entitas.
PropertyEntry.Metadata IProperty metadata untuk properti .
PropertyEntry.IsModified Menunjukkan apakah properti ini ditandai sebagai dimodifikasi, dan memungkinkan status ini diubah.
PropertyEntry.IsTemporary Menunjukkan apakah properti ini ditandai sebagai sementara, dan memungkinkan status ini diubah.

Catatan:

  • Nilai asli properti adalah nilai yang dimiliki properti saat entitas dikueri dari database. Namun, nilai asli tidak tersedia jika entitas terputus dan kemudian secara eksplisit dilampirkan ke DbContext lain, misalnya dengan Attach atau Update. Dalam hal ini, nilai asli yang dikembalikan akan sama dengan nilai saat ini.
  • SaveChanges hanya akan memperbarui properti yang ditandai sebagai dimodifikasi. Atur IsModified ke true untuk memaksa EF Core memperbarui nilai properti tertentu, atau mengaturnya ke false untuk mencegah EF Core memperbarui nilai properti.
  • Nilai sementara biasanya dihasilkan oleh generator nilai EF Core. Mengatur nilai properti saat ini akan menggantikan nilai sementara dengan nilai yang diberikan dan menandai properti sebagai bukan sementara. Atur IsTemporary ke true untuk memaksa nilai menjadi sementara bahkan setelah ditetapkan secara eksplisit.

Bekerja dengan satu navigasi

Beberapa kelebihan beban EntityEntry<TEntity>.Reference, EntityEntry<TEntity>.Collection, dan EntityEntry.Navigation memungkinkan akses ke informasi tentang navigasi individual.

Navigasi referensi ke satu entitas terkait diakses melalui Reference metode . Navigasi referensi menunjuk ke sisi "satu" dari hubungan satu-ke-banyak, dan kedua sisi hubungan satu-ke-satu. Contohnya:

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");

Navigasi juga dapat menjadi kumpulan entitas terkait ketika digunakan untuk sisi "banyak" dari hubungan satu-ke-banyak dan banyak-ke-banyak. Metode Collection ini digunakan untuk mengakses navigasi pengumpulan. Contohnya:

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");

Beberapa operasi umum untuk semua navigasi. Ini dapat diakses untuk navigasi referensi dan pengumpulan menggunakan EntityEntry.Navigation metode . Perhatikan bahwa hanya akses non-generik yang tersedia saat mengakses semua navigasi bersama-sama. Contohnya:

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

Tabel berikut ini meringkas cara menggunakan ReferenceEntry<TEntity,TProperty>, , CollectionEntry<TEntity,TRelatedEntity>dan NavigationEntry:

Anggota NavigationEntry Deskripsi
MemberEntry.CurrentValue Mendapatkan dan mengatur nilai navigasi saat ini. Ini adalah seluruh koleksi untuk navigasi koleksi.
NavigationEntry.Metadata INavigationBase metadata untuk navigasi.
NavigationEntry.IsLoaded Mendapatkan atau menetapkan nilai yang menunjukkan apakah entitas atau koleksi terkait telah dimuat sepenuhnya dari database.
NavigationEntry.Load() Memuat entitas atau koleksi terkait dari database; lihat Pemuatan Eksplisit Data Terkait.
NavigationEntry.Query() Kueri yang akan digunakan EF Core untuk memuat navigasi ini sebagai IQueryable yang dapat disusupi lebih lanjut; lihat Pemuatan Eksplisit Data Terkait.

Bekerja dengan semua properti entitas

EntityEntry.Properties mengembalikan dari IEnumerable<T>PropertyEntry untuk setiap properti entitas. Ini dapat digunakan untuk melakukan tindakan untuk setiap properti entitas. Misalnya, untuk mengatur properti DateTime apa pun ke DateTime.Now:

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

Selain itu, EntityEntry berisi beberapa metode untuk mendapatkan dan mengatur semua nilai properti secara bersamaan. Metode ini menggunakan PropertyValues kelas , yang mewakili kumpulan properti dan nilainya. PropertyValues dapat diperoleh untuk nilai saat ini atau asli, atau untuk nilai seperti yang saat ini disimpan dalam database. Contohnya:

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

Objek PropertyValues ini tidak terlalu berguna sendiri. Namun, mereka dapat dikombinasikan untuk melakukan operasi umum yang diperlukan saat memanipulasi entitas. Ini berguna saat bekerja dengan objek transfer data dan ketika menyelesaikan konflik konkurensi optimis. Bagian berikut menunjukkan beberapa contoh.

Mengatur nilai saat ini atau asli dari entitas atau DTO

Nilai entitas saat ini atau asli dapat diperbarui dengan menyalin nilai dari objek lain. Misalnya, pertimbangkan BlogDto objek transfer data (DTO) dengan properti yang sama dengan jenis entitas:

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

Ini dapat digunakan untuk mengatur nilai saat ini dari entitas yang dilacak menggunakan PropertyValues.SetValues:

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

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

Teknik ini terkadang digunakan saat memperbarui entitas dengan nilai yang diperoleh dari panggilan layanan atau klien dalam aplikasi n-tingkat. Perhatikan bahwa objek yang digunakan tidak harus berjenis sama dengan entitas selama memiliki properti yang namanya cocok dengan entitas. Dalam contoh di atas, instans DTO BlogDto digunakan untuk mengatur nilai entitas yang dilacak Blog saat ini.

Perhatikan bahwa properti hanya akan ditandai sebagai dimodifikasi jika set nilai berbeda dari nilai saat ini.

Mengatur nilai saat ini atau asli dari kamus

Contoh sebelumnya menetapkan nilai dari entitas atau instans DTO. Perilaku yang sama tersedia ketika nilai properti disimpan sebagai pasangan nama/nilai dalam kamus. Contohnya:

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

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

Mengatur nilai saat ini atau asli dari database

Nilai entitas saat ini atau asli dapat diperbarui dengan nilai terbaru dari database dengan memanggil GetDatabaseValues() atau GetDatabaseValuesAsync dan menggunakan objek yang dikembalikan untuk mengatur nilai saat ini atau asli, atau keduanya. Contohnya:

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

Membuat objek kloning yang berisi nilai saat ini, asli, atau database

Objek PropertyValues yang dikembalikan dari CurrentValues, OriginalValues, atau GetDatabaseValues dapat digunakan untuk membuat klon entitas menggunakan PropertyValues.ToObject(). Contohnya:

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

Perhatikan bahwa ToObject mengembalikan instans baru yang tidak dilacak oleh DbContext. Objek yang dikembalikan juga tidak memiliki hubungan apa pun yang diatur ke entitas lain.

Objek kloning dapat berguna untuk menyelesaikan masalah yang terkait dengan pembaruan bersamaan ke database, terutama ketika pengikatan data ke objek dari jenis tertentu. Lihat konkurensi optimis untuk informasi selengkapnya.

Bekerja dengan semua navigasi entitas

EntityEntry.Navigations mengembalikan dari IEnumerable<T>NavigationEntry untuk setiap navigasi entitas. EntityEntry.References dan EntityEntry.Collections melakukan hal yang sama, tetapi dibatasi untuk navigasi referensi atau pengumpulan masing-masing. Ini dapat digunakan untuk melakukan tindakan untuk setiap navigasi entitas. Misalnya, untuk memaksa pemuatan semua entitas terkait:

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

Bekerja dengan semua anggota entitas

Properti reguler dan properti navigasi memiliki status dan perilaku yang berbeda. Oleh karena itu, umum untuk memproses navigasi dan non-navigasi secara terpisah, seperti yang ditunjukkan pada bagian di atas. Namun, terkadang dapat berguna untuk melakukan sesuatu dengan anggota entitas mana pun, terlepas dari apakah itu properti atau navigasi biasa. EntityEntry.Member dan EntityEntry.Members disediakan untuk tujuan ini. Contohnya:

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

Menjalankan kode ini di blog dari sampel menghasilkan output berikut:

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]

Tip

Tampilan debug pelacak perubahan menampilkan informasi seperti ini. Tampilan debug untuk seluruh pelacak perubahan dihasilkan dari individu EntityEntry.DebugView dari setiap entitas yang dilacak.

Temukan dan FindAsync

DbContext.Find, , DbContext.FindAsyncDbSet<TEntity>.Find, dan DbSet<TEntity>.FindAsync dirancang untuk pencarian yang efisien dari satu entitas ketika kunci utamanya diketahui. Temukan pemeriksaan pertama apakah entitas sudah dilacak, dan jika demikian segera mengembalikan entitas. Kueri database hanya dibuat jika entitas tidak dilacak secara lokal. Misalnya, pertimbangkan kode ini yang memanggil Temukan dua kali untuk entitas yang sama:

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.");

Output dari kode ini (termasuk pengelogan EF Core) saat menggunakan SQLite adalah:

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.

Perhatikan bahwa panggilan pertama tidak menemukan entitas secara lokal dan menjalankan kueri database. Sebaliknya, panggilan kedua mengembalikan instans yang sama tanpa mengkueri database karena sudah dilacak.

Temukan mengembalikan null jika entitas dengan kunci yang diberikan tidak dilacak secara lokal dan tidak ada di database.

Kunci komposit

Temukan juga dapat digunakan dengan kunci komposit. Misalnya, pertimbangkan OrderLine entitas dengan kunci komposit yang terdiri dari ID pesanan dan ID produk:

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

    //...
}

Kunci komposit harus dikonfigurasi DbContext.OnModelCreating untuk menentukan bagian kunci dan urutannya. Contohnya:

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

Perhatikan bahwa OrderId adalah bagian pertama dari kunci dan ProductId merupakan bagian kedua dari kunci. Urutan ini harus digunakan saat meneruskan nilai kunci ke Temukan. Contohnya:

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

Menggunakan ChangeTracker.Entries untuk mengakses semua entitas yang dilacak

Sejauh ini kami hanya mengakses satu per satu EntityEntry . ChangeTracker.Entries() mengembalikan EntityEntry untuk setiap entitas yang saat ini dilacak oleh DbContext. Contohnya:

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

Kode ini menghasilkan output berikut:

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

Perhatikan bahwa entri untuk blog dan posting dikembalikan. Hasilnya dapat difilter ke jenis entitas tertentu menggunakan ChangeTracker.Entries<TEntity>() kelebihan beban umum:

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

Output dari kode ini menunjukkan bahwa hanya posting yang dikembalikan:

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

Selain itu, menggunakan kelebihan beban umum mengembalikan instans generik EntityEntry<TEntity> . Inilah yang memungkinkan akses seperti fasih ke Id properti dalam contoh ini.

Jenis generik yang digunakan untuk pemfilteran tidak harus menjadi jenis entitas yang dipetakan; jenis dasar atau antarmuka yang tidak dipetakan dapat digunakan sebagai gantinya. Misalnya, jika semua jenis entitas dalam model menerapkan antarmuka yang menentukan properti kuncinya:

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

Kemudian antarmuka ini dapat digunakan untuk bekerja dengan kunci entitas apa pun yang dilacak dengan cara yang sangat ditik. Contohnya:

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

Menggunakan DbSet.Local untuk mengkueri entitas yang dilacak

Kueri EF Core selalu dijalankan pada database, dan hanya mengembalikan entitas yang telah disimpan ke database. DbSet<TEntity>.Local menyediakan mekanisme untuk mengkueri DbContext untuk entitas lokal yang dilacak.

Karena DbSet.Local digunakan untuk mengkueri entitas yang dilacak, biasanya memuat entitas ke dalam DbContext lalu bekerja dengan entitas yang dimuat tersebut. Ini terutama berlaku untuk pengikatan data, tetapi juga dapat berguna dalam situasi lain. Misalnya, dalam kode berikut database pertama kali dikueri untuk semua blog dan postingan. Metode Load ekstensi digunakan untuk menjalankan kueri ini dengan hasil yang dilacak oleh konteks tanpa dikembalikan langsung ke aplikasi. (Menggunakan ToList atau serupa memiliki efek yang sama tetapi dengan overhead pembuatan daftar yang dikembalikan, yang tidak diperlukan di sini.) Contoh kemudian menggunakan DbSet.Local untuk mengakses entitas yang dilacak secara lokal:

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

Perhatikan bahwa, tidak seperti ChangeTracker.Entries(), DbSet.Local mengembalikan instans entitas secara langsung. EntitasEntry dapat, tentu saja, selalu diperoleh untuk entitas yang dikembalikan dengan memanggil DbContext.Entry.

Tampilan lokal

DbSet<TEntity>.Local mengembalikan tampilan entitas yang dilacak secara lokal yang mencerminkan entitas tersebut saat ini EntityState . Secara khusus, ini berarti bahwa:

  • Added entitas disertakan. Perhatikan bahwa ini bukan kasus untuk kueri EF Core normal, karena Added entitas belum ada dalam database dan karena itu tidak pernah dikembalikan oleh kueri database.
  • Deleted entitas dikecualikan. Perhatikan bahwa ini sekali lagi tidak terjadi untuk kueri EF Core normal, karena Deleted entitas masih ada dalam database dan sebagainya dikembalikan oleh kueri database.

Semua ini berarti bahwa DbSet.Local melihat data yang mencerminkan status konseptual grafik entitas saat ini, dengan Added entitas disertakan dan Deleted entitas dikecualikan. Ini cocok dengan status database apa yang diharapkan setelah SaveChanges dipanggil.

Ini biasanya tampilan ideal untuk pengikatan data, karena disajikan kepada pengguna data karena mereka memahaminya berdasarkan perubahan yang dibuat oleh aplikasi.

Kode berikut menunjukkan ini dengan menandai satu postingan sebagai Deleted lalu menambahkan postingan baru, menandainya sebagai 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}");
}

Output dari kode ini adalah:

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

Perhatikan bahwa postingan yang dihapus dihapus dari tampilan lokal, dan postingan yang ditambahkan disertakan.

Menggunakan Lokal untuk menambahkan dan menghapus entitas

DbSet<TEntity>.Local mengembalikan instans dari LocalView<TEntity>. Ini adalah implementasi dari ICollection<T> yang menghasilkan dan merespons pemberitahuan ketika entitas ditambahkan dan dihapus dari koleksi. (Ini adalah konsep yang sama dengan ObservableCollection<T>, tetapi diimplementasikan sebagai proyeksi atas entri pelacakan perubahan Inti EF yang ada, bukan sebagai koleksi independen.)

Pemberitahuan tampilan lokal dikaitkan ke pelacakan perubahan DbContext sehingga tampilan lokal tetap sinkron dengan DbContext. Khususnya:

  • Menambahkan entitas baru menyebabkannya DbSet.Local dilacak oleh DbContext, biasanya dalam status Added . (Jika entitas sudah memiliki nilai kunci yang dihasilkan, maka entitas tersebut dilacak sebagai Unchanged gantinya.)
  • Menghapus entitas dari DbSet.Local penyebabnya ditandai sebagai Deleted.
  • Entitas yang dilacak oleh DbContext akan secara otomatis muncul dalam DbSet.Local koleksi. Misalnya, menjalankan kueri untuk membawa lebih banyak entitas secara otomatis menyebabkan tampilan lokal diperbarui.
  • Entitas yang ditandai sebagai Deleted akan dihapus dari koleksi lokal secara otomatis.

Ini berarti tampilan lokal dapat digunakan untuk memanipulasi entitas yang dilacak hanya dengan menambahkan dan menghapus dari koleksi. Misalnya, mari kita ubah kode contoh sebelumnya untuk menambahkan dan menghapus postingan dari koleksi lokal:

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

Output tetap tidak berubah dari contoh sebelumnya karena perubahan yang dilakukan pada tampilan lokal disinkronkan dengan DbContext.

Menggunakan tampilan lokal untuk pengikatan data Formulir Windows atau WPF

DbSet<TEntity>.Local membentuk dasar untuk pengikatan data ke entitas EF Core. Namun, baik Formulir Windows maupun WPF berfungsi paling baik saat digunakan dengan jenis tertentu untuk memberi tahu koleksi yang mereka harapkan. Tampilan lokal mendukung pembuatan jenis koleksi khusus ini:

Contohnya:

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

Lihat Mulai menggunakan WPF untuk informasi selengkapnya tentang pengikatan data WPF dengan EF Core, dan Mulai menggunakan Formulir Windows untuk informasi selengkapnya tentang pengikatan data Formulir Windows dengan EF Core.

Tip

Tampilan lokal untuk instans DbSet tertentu dibuat dengan malas saat pertama kali diakses lalu di-cache. Pembuatan LocalView itu sendiri cepat dan tidak menggunakan memori yang signifikan. Namun, itu memanggil DetectChanges, yang dapat lambat untuk sejumlah besar entitas. Koleksi yang dibuat oleh ToObservableCollection dan ToBindingList juga dibuat dengan malas dan kemudian di-cache. Kedua metode ini membuat koleksi baru, yang bisa lambat dan menggunakan banyak memori ketika ribuan entitas terlibat.