Bagikan melalui


Data Lokal

Menjalankan kueri LINQ secara langsung terhadap DbSet akan selalu mengirim kueri ke database, tetapi Anda dapat mengakses data yang saat ini dalam memori menggunakan properti DbSet.Local. Anda juga dapat mengakses informasi tambahan yang dilacak EF tentang entitas Anda menggunakan metode DbContext.Entry dan DbContext.ChangeTracker.Entries. Teknik yang ditunjukkan dalam topik ini berlaku sama untuk model yang dibuat dengan Perancang EF dan Code First.

Menggunakan Lokal untuk melihat data lokal

Properti Lokal DbSet menyediakan akses sederhana ke entitas set yang saat ini sedang dilacak oleh konteks dan belum ditandai sebagai Dihapus. Mengakses properti Lokal tidak pernah menyebabkan kueri dikirim ke database. Ini berarti biasanya digunakan setelah kueri dilakukan. Metode Muat ekstensi dapat digunakan untuk menjalankan kueri sehingga konteks melacak hasilnya. Contohnya:

using (var context = new BloggingContext())
{
    // Load all blogs from the database into the context
    context.Blogs.Load();

    // Add a new blog to the context
    context.Blogs.Add(new Blog { Name = "My New Blog" });

    // Mark one of the existing blogs as Deleted
    context.Blogs.Remove(context.Blogs.Find(1));

    // Loop over the blogs in the context.
    Console.WriteLine("In Local: ");
    foreach (var blog in context.Blogs.Local)
    {
        Console.WriteLine(
            "Found {0}: {1} with state {2}",
            blog.BlogId,  
            blog.Name,
            context.Entry(blog).State);
    }

    // Perform a query against the database.
    Console.WriteLine("\nIn DbSet query: ");
    foreach (var blog in context.Blogs)
    {
        Console.WriteLine(
            "Found {0}: {1} with state {2}",
            blog.BlogId,  
            blog.Name,
            context.Entry(blog).State);
    }
}

Jika kita memiliki dua blog dalam database - 'ADO.NET Blog' dengan BlogId 1 dan 'Blog Visual Studio' dengan BlogId 2 - kita bisa mengharapkan output berikut:

In Local:
Found 0: My New Blog with state Added
Found 2: The Visual Studio Blog with state Unchanged

In DbSet query:
Found 1: ADO.NET Blog with state Deleted
Found 2: The Visual Studio Blog with state Unchanged

Ini menggambarkan tiga poin:

  • Blog baru 'Blog Baru Saya' disertakan dalam koleksi Lokal meskipun belum disimpan ke database. Blog ini memiliki kunci utama nol karena database belum menghasilkan kunci nyata untuk entitas.
  • 'blog ADO.NET' tidak termasuk dalam koleksi lokal meskipun masih dilacak oleh konteks. Ini karena kami menghapusnya dari DbSet sehingga menandainya sebagai dihapus.
  • Ketika DbSet digunakan untuk melakukan kueri, blog yang ditandai untuk dihapus (ADO.NET Blog) disertakan dalam hasil dan blog baru (Blog Baru Saya) yang belum disimpan ke database tidak disertakan dalam hasilnya. Ini karena DbSet melakukan kueri terhadap database dan hasil yang dikembalikan selalu mencerminkan apa yang ada dalam database.

Menggunakan Lokal untuk menambahkan dan menghapus entitas dari konteks

Properti Lokal di DbSet mengembalikan ObservableCollection dengan peristiwa yang dikaitkan sehingga tetap sinkron dengan konten konteks. Ini berarti bahwa entitas dapat ditambahkan atau dihapus dari koleksi Lokal atau DbSet. Ini juga berarti bahwa kueri yang membawa entitas baru ke dalam konteks akan mengakibatkan koleksi Lokal diperbarui dengan entitas tersebut. Contohnya:

using (var context = new BloggingContext())
{
    // Load some posts from the database into the context
    context.Posts.Where(p => p.Tags.Contains("entity-framework")).Load();  

    // Get the local collection and make some changes to it
    var localPosts = context.Posts.Local;
    localPosts.Add(new Post { Name = "What's New in EF" });
    localPosts.Remove(context.Posts.Find(1));  

    // Loop over the posts in the context.
    Console.WriteLine("In Local after entity-framework query: ");
    foreach (var post in context.Posts.Local)
    {
        Console.WriteLine(
            "Found {0}: {1} with state {2}",
            post.Id,  
            post.Title,
            context.Entry(post).State);
    }

    var post1 = context.Posts.Find(1);
    Console.WriteLine(
        "State of post 1: {0} is {1}",
        post1.Name,  
        context.Entry(post1).State);  

    // Query some more posts from the database
    context.Posts.Where(p => p.Tags.Contains("asp.net")).Load();  

    // Loop over the posts in the context again.
    Console.WriteLine("\nIn Local after asp.net query: ");
    foreach (var post in context.Posts.Local)
    {
        Console.WriteLine(
            "Found {0}: {1} with state {2}",
            post.Id,  
            post.Title,
            context.Entry(post).State);
    }
}

Dengan asumsi kami memiliki beberapa postingan yang ditandai dengan 'entity-framework' dan 'asp.net' output mungkin terlihat seperti ini:

In Local after entity-framework query:
Found 3: EF Designer Basics with state Unchanged
Found 5: EF Code First Basics with state Unchanged
Found 0: What's New in EF with state Added
State of post 1: EF Beginners Guide is Deleted

In Local after asp.net query:
Found 3: EF Designer Basics with state Unchanged
Found 5: EF Code First Basics with state Unchanged
Found 0: What's New in EF with state Added
Found 4: ASP.NET Beginners Guide with state Unchanged

Ini menggambarkan tiga poin:

  • Posting baru 'Apa yang Baru di EF' yang ditambahkan ke Koleksi lokal menjadi dilacak oleh konteks dalam status Ditambahkan. Oleh karena itu, ini akan dimasukkan ke dalam database ketika SaveChanges dipanggil.
  • Postingan yang dihapus dari Koleksi lokal (Panduan Pemula EF) sekarang ditandai sebagai dihapus dalam konteks. Oleh karena itu akan dihapus dari database ketika SaveChanges dipanggil.
  • Posting tambahan (ASP.NET Panduan Pemula) yang dimuat ke dalam konteks dengan kueri kedua secara otomatis ditambahkan ke koleksi Lokal.

Satu hal terakhir yang perlu diperhatikan tentang Lokal adalah karena performa ObservableCollection tidak bagus untuk sejumlah besar entitas. Oleh karena itu, jika Anda berurusan dengan ribuan entitas dalam konteks Anda, mungkin tidak disarankan untuk menggunakan Lokal.

Menggunakan Pengikatan data Lokal untuk WPF

Properti Lokal di DbSet dapat digunakan langsung untuk pengikatan data dalam aplikasi WPF karena merupakan instans ObservableCollection. Seperti yang dijelaskan di bagian sebelumnya, ini berarti secara otomatis akan tetap sinkron dengan konten konteks dan konten konteks akan secara otomatis tetap sinkron dengannya. Perhatikan bahwa Anda perlu mengisi koleksi Lokal terlebih dahulu dengan data agar ada apa pun yang harus diikat karena Lokal tidak pernah menyebabkan kueri database.

Ini bukan tempat yang sesuai untuk sampel pengikatan data WPF lengkap tetapi elemen kuncinya adalah:

  • Menyiapkan sumber pengikatan
  • Mengikatnya ke properti Lokal dari set Anda
  • Isi Lokal menggunakan kueri ke database.

Pengikatan WPF ke properti navigasi

Jika Anda melakukan pengikatan data master/detail, Anda mungkin ingin mengikat tampilan detail ke properti navigasi salah satu entitas Anda. Cara mudah untuk membuat pekerjaan ini adalah dengan menggunakan ObservableCollection untuk properti navigasi. Contohnya:

public class Blog
{
    private readonly ObservableCollection<Post> _posts =
        new ObservableCollection<Post>();

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

    public virtual ObservableCollection<Post> Posts
    {
        get { return _posts; }
    }
}

Menggunakan Lokal untuk membersihkan entitas di SaveChanges

Dalam kebanyakan kasus, entitas yang dihapus dari properti navigasi tidak akan secara otomatis ditandai sebagai dihapus dalam konteks. Misalnya, jika Anda menghapus objek Postingan dari koleksi Blog.Posts, maka postingan tersebut tidak akan dihapus secara otomatis saat SaveChanges dipanggil. Jika Anda perlu menghapusnya maka Anda mungkin perlu menemukan entitas yang menggintai ini dan menandainya sebagai dihapus sebelum memanggil SaveChanges atau sebagai bagian dari SaveChanges yang ditimpa. Contohnya:

public override int SaveChanges()
{
    foreach (var post in this.Posts.Local.ToList())
    {
        if (post.Blog == null)
        {
            this.Posts.Remove(post);
        }
    }

    return base.SaveChanges();
}

Kode di atas menggunakan koleksi Lokal untuk menemukan semua postingan dan menandai apa pun yang tidak memiliki referensi blog sebagai dihapus. Panggilan ToList diperlukan karena jika tidak, koleksi akan dimodifikasi oleh Hapus panggilan saat sedang dijumlahkan. Dalam sebagian besar situasi lain, Anda dapat mengkueri langsung terhadap properti Lokal tanpa menggunakan ToList terlebih dahulu.

Menggunakan Local dan ToBindingList untuk pengikatan data Formulir Windows

Formulir Windows tidak mendukung pengikatan data keakuratan penuh menggunakan ObservableCollection secara langsung. Namun, Anda masih dapat menggunakan properti DbSet Local untuk pengikatan data untuk mendapatkan semua manfaat yang dijelaskan di bagian sebelumnya. Hal ini dicapai melalui metode ekstensi ToBindingList yang membuat implementasi IBindingList yang didukung oleh Local ObservableCollection.

Ini bukan tempat yang sesuai untuk sampel pengikatan data Formulir Windows lengkap tetapi elemen utamanya adalah:

  • Menyiapkan sumber pengikatan objek
  • Ikat ke properti Lokal set Anda menggunakan Local.ToBindingList()
  • Mengisi Lokal menggunakan kueri ke database

Mendapatkan informasi terperinci tentang entitas terlacak

Banyak contoh dalam seri ini menggunakan metode Entri untuk mengembalikan instans DbEntityEntry untuk entitas. Objek entri ini kemudian bertindak sebagai titik awal untuk mengumpulkan informasi tentang entitas seperti statusnya saat ini, serta untuk melakukan operasi pada entitas seperti secara eksplisit memuat entitas terkait.

Metode Entri mengembalikan objek DbEntityEntry untuk banyak atau semua entitas yang dilacak oleh konteks. Ini memungkinkan Anda untuk mengumpulkan informasi atau melakukan operasi pada banyak entitas daripada hanya satu entri. Contohnya:

using (var context = new BloggingContext())
{
    // Load some entities into the context
    context.Blogs.Load();
    context.Authors.Load();
    context.Readers.Load();

    // Make some changes
    context.Blogs.Find(1).Title = "The New ADO.NET Blog";
    context.Blogs.Remove(context.Blogs.Find(2));
    context.Authors.Add(new Author { Name = "Jane Doe" });
    context.Readers.Find(1).Username = "johndoe1987";

    // Look at the state of all entities in the context
    Console.WriteLine("All tracked entities: ");
    foreach (var entry in context.ChangeTracker.Entries())
    {
        Console.WriteLine(
            "Found entity of type {0} with state {1}",
            ObjectContext.GetObjectType(entry.Entity.GetType()).Name,
            entry.State);
    }

    // Find modified entities of any type
    Console.WriteLine("\nAll modified entities: ");
    foreach (var entry in context.ChangeTracker.Entries()
                              .Where(e => e.State == EntityState.Modified))
    {
        Console.WriteLine(
            "Found entity of type {0} with state {1}",
            ObjectContext.GetObjectType(entry.Entity.GetType()).Name,
            entry.State);
    }

    // Get some information about just the tracked blogs
    Console.WriteLine("\nTracked blogs: ");
    foreach (var entry in context.ChangeTracker.Entries<Blog>())
    {
        Console.WriteLine(
            "Found Blog {0}: {1} with original Name {2}",
            entry.Entity.BlogId,  
            entry.Entity.Name,
            entry.Property(p => p.Name).OriginalValue);
    }

    // Find all people (author or reader)
    Console.WriteLine("\nPeople: ");
    foreach (var entry in context.ChangeTracker.Entries<IPerson>())
    {
        Console.WriteLine("Found Person {0}", entry.Entity.Name);
    }
}

Anda akan melihat kami memperkenalkan kelas Penulis dan Pembaca ke dalam contoh - kedua kelas ini mengimplementasikan antarmuka IPerson.

public class Author : IPerson
{
    public int AuthorId { get; set; }
    public string Name { get; set; }
    public string Biography { get; set; }
}

public class Reader : IPerson
{
    public int ReaderId { get; set; }
    public string Name { get; set; }
    public string Username { get; set; }
}

public interface IPerson
{
    string Name { get; }
}

Mari kita asumsikan kita memiliki data berikut dalam database:

Blog dengan BlogId = 1 dan Nama = 'ADO.NET Blog'
Blog dengan BlogId = 2 dan Nama = 'Blog Visual Studio'
Blog dengan BlogId = 3 dan Nama = '.NET Framework Blog'
Penulis dengan AuthorId = 1 dan Name = 'Joe Bloggs'
Pembaca dengan ReaderId = 1 dan Nama = 'John Doe'

Output dari menjalankan kode adalah:

All tracked entities:
Found entity of type Blog with state Modified
Found entity of type Blog with state Deleted
Found entity of type Blog with state Unchanged
Found entity of type Author with state Unchanged
Found entity of type Author with state Added
Found entity of type Reader with state Modified

All modified entities:
Found entity of type Blog with state Modified
Found entity of type Reader with state Modified

Tracked blogs:
Found Blog 1: The New ADO.NET Blog with original Name ADO.NET Blog
Found Blog 2: The Visual Studio Blog with original Name The Visual Studio Blog
Found Blog 3: .NET Framework Blog with original Name .NET Framework Blog

People:
Found Person John Doe
Found Person Joe Bloggs
Found Person Jane Doe

Contoh-contoh ini mengilustrasikan beberapa poin:

  • Metode Entri mengembalikan entri untuk entitas di semua status, termasuk Dihapus. Bandingkan ini dengan Lokal yang mengecualikan entitas yang Dihapus.
  • Entri untuk semua jenis entitas dikembalikan ketika metode Entri non-generik digunakan. Ketika metode entri generik digunakan entri hanya dikembalikan untuk entitas yang merupakan instans dari jenis generik. Ini digunakan di atas untuk mendapatkan entri untuk semua blog. Ini juga digunakan untuk mendapatkan entri untuk semua entitas yang mengimplementasikan IPerson. Ini menunjukkan bahwa jenis generik tidak harus menjadi jenis entitas aktual.
  • LINQ ke Objek dapat digunakan untuk memfilter hasil yang dikembalikan. Ini digunakan di atas untuk menemukan entitas dari jenis apa pun selama mereka dimodifikasi.

Perhatikan bahwa instans DbEntityEntry selalu berisi Entitas non-null. Entri hubungan dan entri stub tidak direpresentasikan sebagai instans DbEntityEntry sehingga tidak perlu memfilternya.