Bagikan melalui


Pengikatan data dengan WinForms

Panduan langkah demi langkah ini menunjukkan cara mengikat jenis POCO ke kontrol Window Forms (WinForms) dalam formulir "detail master". Aplikasi ini menggunakan Entity Framework untuk mengisi objek dengan data dari database, melacak perubahan, dan menyimpan data ke database.

Model mendefinisikan dua jenis yang berpartisipasi dalam hubungan satu-ke-banyak: Kategori (utama\master) dan Produk (dependent\detail). Kemudian, alat Visual Studio digunakan untuk mengikat jenis yang ditentukan dalam model ke kontrol WinForms. Kerangka kerja pengikatan data WinForms memungkinkan navigasi antara objek terkait: memilih baris dalam tampilan master menyebabkan tampilan detail diperbarui dengan data anak yang sesuai.

Cuplikan layar dan daftar kode dalam panduan ini diambil dari Visual Studio 2013 tetapi Anda dapat menyelesaikan panduan ini dengan Visual Studio 2012 atau Visual Studio 2010.

Prasyarat

Anda harus menginstal Visual Studio 2013, Visual Studio 2012, atau Visual Studio 2010 untuk menyelesaikan panduan ini.

Jika Anda menggunakan Visual Studio 2010, Anda juga harus menginstal NuGet. Untuk informasi selengkapnya, lihat Menginstal NuGet.

Membuat Aplikasi

  • Membuka Visual Studio
  • File -> Baru -> Proyek...
  • Pilih Windows di panel kiri dan Formulir Windows Aplikasi di panel kanan
  • Masukkan WinFormswithEFSample sebagai nama
  • Pilih OK

Menginstal paket Entity Framework NuGet

  • Di Penjelajah Solusi, klik kanan pada proyek WinFormswithEFSample
  • Pilih Kelola Paket NuGet...
  • Dalam dialog Kelola Paket NuGet, Pilih tab Online dan pilih paket EntityFramework
  • Klik Instal

    Catatan

    Selain rakitan EntityFramework, referensi ke System.ComponentModel.DataAnnotations juga ditambahkan. Jika proyek memiliki referensi ke System.Data.Entity, maka proyek akan dihapus saat paket EntityFramework diinstal. Rakitan System.Data.Entity tidak lagi digunakan untuk aplikasi Entity Framework 6.

Menerapkan IListSource untuk Koleksi

Properti koleksi harus menerapkan antarmuka IListSource untuk mengaktifkan pengikatan data dua arah dengan pengurutan saat menggunakan Formulir Windows. Untuk melakukan ini, kita akan memperluas ObservableCollection untuk menambahkan fungsionalitas IListSource.

  • Tambahkan kelas ObservableListSource ke proyek:
    • Klik kanan pada nama proyek
    • Pilih Tambahkan -> Item Baru
    • Pilih Kelas dan masukkan ObservableListSource untuk nama kelas
  • Ganti kode yang dihasilkan secara default dengan kode berikut:

Kelas ini memungkinkan pengikatan data dua arah serta pengurutan. Kelas ini berasal dari ObservableCollection<T> dan menambahkan implementasi eksplisit IListSource. Metode GetList() IListSource diimplementasikan untuk mengembalikan implementasi IBindingList yang tetap sinkron dengan ObservableCollection. Implementasi IBindingList yang dihasilkan oleh ToBindingList mendukung pengurutan. Metode ekstensi ToBindingList didefinisikan dalam perakitan EntityFramework.

    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Data.Entity;

    namespace WinFormswithEFSample
    {
        public class ObservableListSource<T> : ObservableCollection<T>, IListSource
            where T : class
        {
            private IBindingList _bindingList;

            bool IListSource.ContainsListCollection { get { return false; } }

            IList IListSource.GetList()
            {
                return _bindingList ?? (_bindingList = this.ToBindingList());
            }
        }
    }

Tentukan Model

Dalam panduan ini, Anda dapat memilih untuk menerapkan model menggunakan Code First atau EF Designer. Lengkapi salah satu dari dua bagian berikut.

Opsi 1: Tentukan Model menggunakan Kode Terlebih Dahulu

Bagian ini memperlihatkan cara membuat model dan database terkait menggunakan Kode Pertama. Lewati ke bagian berikutnya (Opsi 2: Tentukan model menggunakan Database First) jika Anda lebih suka menggunakan Database First untuk merekayasa balik model Anda dari database menggunakan perancang EF

Saat menggunakan pengembangan Code First, Anda biasanya mulai dengan menulis kelas .NET Framework yang menentukan model konseptual (domain) Anda.

  • Menambahkan kelas Produk baru ke proyek
  • Ganti kode yang dihasilkan secara default dengan kode berikut:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace WinFormswithEFSample
    {
        public class Product
        {
            public int ProductId { get; set; }
            public string Name { get; set; }

            public int CategoryId { get; set; }
            public virtual Category Category { get; set; }
        }
    }
  • Tambahkan kelas Kategori ke proyek.
  • Ganti kode yang dihasilkan secara default dengan kode berikut:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    namespace WinFormswithEFSample
    {
        public class Category
        {
            private readonly ObservableListSource<Product> _products =
                    new ObservableListSource<Product>();

            public int CategoryId { get; set; }
            public string Name { get; set; }
            public virtual ObservableListSource<Product> Products { get { return _products; } }
        }
    }

Selain menentukan entitas, Anda perlu menentukan kelas yang berasal dari DbContext dan mengekspos properti DbSet<TEntity> . Properti DbSet memberi tahu konteks jenis mana yang ingin Anda sertakan dalam model. Jenis DbContext dan DbSet didefinisikan dalam rakitan EntityFramework.

Instans jenis turunan DbContext mengelola objek entitas selama run time, yang mencakup mengisi objek dengan data dari database, pelacakan perubahan, dan menyimpan data ke database.

  • Tambahkan kelas ProductContext baru ke proyek.
  • Ganti kode yang dihasilkan secara default dengan kode berikut:
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Linq;
    using System.Text;

    namespace WinFormswithEFSample
    {
        public class ProductContext : DbContext
        {
            public DbSet<Category> Categories { get; set; }
            public DbSet<Product> Products { get; set; }
        }
    }

Mengompilasi proyek.

Opsi 2: Tentukan model menggunakan Database First

Bagian ini memperlihatkan cara menggunakan Database First untuk merekayasa balik model Anda dari database menggunakan perancang EF. Jika Anda menyelesaikan bagian sebelumnya (Opsi 1: Tentukan model menggunakan Kode Pertama), lewati bagian ini dan langsung masuk ke bagian Pemuatan Malas .

Membuat Database yang Sudah Ada

Biasanya ketika Anda menargetkan database yang sudah ada, database tersebut sudah akan dibuat, tetapi untuk panduan ini kita perlu membuat database untuk diakses.

Server database yang diinstal dengan Visual Studio berbeda tergantung pada versi Visual Studio yang telah Anda instal:

  • Jika Anda menggunakan Visual Studio 2010, Anda akan membuat database SQL Express.
  • Jika Anda menggunakan Visual Studio 2012, Maka Anda akan membuat database LocalDB .

Mari kita lanjutkan dan hasilkan database.

  • Tampilan -> Penjelajah Server

  • Klik kanan pada Data Koneksi ions -> Tambahkan Koneksi ion...

  • Jika Anda belum tersambung ke database dari Server Explorer sebelum Anda harus memilih Microsoft SQL Server sebagai sumber data

    Change Data Source

  • Koneksi ke LocalDB atau SQL Express, tergantung pada mana yang telah Anda instal, dan masukkan Produk sebagai nama database

    Add Connection LocalDB

    Add Connection Express

  • Pilih OK dan Anda akan ditanya apakah Anda ingin membuat database baru, pilih Ya

    Create Database

  • Database baru sekarang akan muncul di Server Explorer, klik kanan padanya dan pilih Kueri Baru

  • Salin SQL berikut ke dalam kueri baru, lalu klik kanan pada kueri dan pilih Jalankan

    CREATE TABLE [dbo].[Categories] (
        [CategoryId] [int] NOT NULL IDENTITY,
        [Name] [nvarchar](max),
        CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
    )

    CREATE TABLE [dbo].[Products] (
        [ProductId] [int] NOT NULL IDENTITY,
        [Name] [nvarchar](max),
        [CategoryId] [int] NOT NULL,
        CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
    )

    CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])

    ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE

Reverse Engineer Model

Kita akan menggunakan Entity Framework Designer, yang disertakan sebagai bagian dari Visual Studio, untuk membuat model kita.

  • Project -> Tambahkan Item Baru...

  • Pilih Data dari menu sebelah kiri lalu ADO.NET Model Data Entitas

  • Masukkan ProductModel sebagai nama dan klik OK

  • Ini meluncurkan Wizard Model Data Entitas

  • Pilih Buat dari Database dan klik Berikutnya

    Choose Model Contents

  • Pilih koneksi ke database yang Anda buat di bagian pertama, masukkan ProductContext sebagai nama string koneksi dan klik Berikutnya

    Choose Your Connection

  • Klik kotak centang di samping 'Tabel' untuk mengimpor semua tabel dan klik 'Selesai'

    Choose Your Objects

Setelah proses insinyur terbalik menyelesaikan model baru ditambahkan ke proyek Anda dan dibuka untuk Anda lihat di Perancang Kerangka Kerja Entitas. File App.config juga telah ditambahkan ke proyek Anda dengan detail koneksi untuk database.

Langkah Tambahan di Visual Studio 2010

Jika Anda bekerja di Visual Studio 2010 maka Anda harus memperbarui perancang EF untuk menggunakan pembuatan kode EF6.

  • Klik kanan pada tempat kosong model Anda di Perancang EF dan pilih Tambahkan Item Pembuatan Kode...
  • Pilih Templat Online dari menu sebelah kiri dan cari DbContext
  • Pilih Generator EF 6.x DbContext untuk C#, masukkan ProductsModel sebagai nama dan klik Tambahkan

Memperbarui pembuatan kode untuk pengikatan data

EF menghasilkan kode dari model Anda menggunakan templat T4. Templat yang dikirim dengan Visual Studio atau diunduh dari galeri Visual Studio ditujukan untuk penggunaan tujuan umum. Ini berarti bahwa entitas yang dihasilkan dari templat ini memiliki properti T> ICollection<sederhana. Namun, ketika melakukan pengikatan data, diinginkan untuk memiliki properti pengumpulan yang mengimplementasikan IListSource. Inilah sebabnya mengapa kami membuat kelas ObservableListSource di atas dan kami sekarang akan memodifikasi templat untuk menggunakan kelas ini.

  • Buka Penjelajah Solusi dan temukan file ProductModel.edmx

  • Temukan file ProductModel.tt yang akan disarangkan di bawah file ProductModel.edmx

    Product Model Template

  • Klik dua kali pada file ProductModel.tt untuk membukanya di editor Visual Studio

  • Temukan dan ganti dua kemunculan "ICollection" dengan "ObservableListSource". Ini terletak di sekitar jalur 296 dan 484.

  • Temukan dan ganti kemunculan pertama "HashSet" dengan "ObservableListSource". Kejadian ini terletak di sekitar baris 50. Jangan ganti kemunculan kedua HashSet yang ditemukan nanti dalam kode.

  • Simpan file ProductModel.tt. Ini harus menyebabkan kode untuk entitas diregenerasi. Jika kode tidak diregenerasi secara otomatis, klik kanan pada ProductModel.tt dan pilih "Jalankan Alat Kustom".

Jika Anda sekarang membuka file Category.cs (yang disarangkan di bawah ProductModel.tt) maka Anda akan melihat bahwa koleksi Produk memiliki jenis Produk> ObservableListSource<.

Mengompilasi proyek.

Pemuatan Lambat

Properti Produk pada kelas Kategori dan properti Kategori pada kelas Produk adalah properti navigasi. Dalam Kerangka Kerja Entitas, properti navigasi menyediakan cara untuk menavigasi hubungan antara dua jenis entitas.

EF memberi Anda opsi untuk memuat entitas terkait dari database secara otomatis saat pertama kali Anda mengakses properti navigasi. Dengan jenis pemuatan ini (disebut pemuatan malas), ketahuilah bahwa pertama kali Anda mengakses setiap properti navigasi, kueri terpisah akan dijalankan terhadap database jika konten belum dalam konteks.

Saat menggunakan jenis entitas POCO, EF mencapai pemuatan malas dengan membuat instans jenis proksi turunan selama runtime lalu menimpa properti virtual di kelas Anda untuk menambahkan hook pemuatan. Untuk mendapatkan pemuatan objek terkait yang malas, Anda harus mendeklarasikan getter properti navigasi sebagai publik dan virtual (Dapat Diganti di Visual Basic), dan kelas Anda tidak boleh disegel(NotOverridable di Visual Basic). Saat menggunakan properti navigasi Database First secara otomatis dibuat virtual untuk mengaktifkan pemuatan malas. Di bagian Kode Pertama, kami memilih untuk membuat properti navigasi virtual karena alasan yang sama

Mengikat Objek ke Kontrol

Tambahkan kelas yang didefinisikan dalam model sebagai sumber data untuk aplikasi WinForms ini.

  • Dari menu utama, pilih Proyek -> Tambahkan Sumber Data Baru ... (di Visual Studio 2010, Anda perlu memilih Data -> Tambahkan Sumber Data Baru...)

  • Di jendela Pilih Tipe Sumber Data, pilih Objek dan klik Berikutnya

  • Dalam dialog Pilih Objek Data, buka WinFormswithEFSample dua kali dan pilih Kategori Tidak perlu memilih sumber data Produk, karena kita akan mendapatkannya melalui properti Produk pada sumber data Kategori.

    Data Source

  • Klik Selesai. Jika jendela Sumber Data tidak muncul, pilih Tampilkan -> Sumber Data Windows> Lainnya

  • Tekan ikon sematkan, sehingga jendela Sumber Data tidak disembunyikan secara otomatis. Anda mungkin perlu menekan tombol refresh jika jendela sudah terlihat.

    Data Source 2

  • Di Penjelajah Solusi, klik dua kali file Form1.cs untuk membuka formulir utama dalam perancang.

  • Pilih sumber data Kategori dan seret pada formulir. Secara default, kontrol toolbar DataGridView (categoryDataGridView) dan Navigasi baru ditambahkan ke perancang. Kontrol ini terikat ke komponen BindingSource (categoryBindingSource) dan Binding Navigator (categoryBindingNavigator) yang juga dibuat.

  • Edit kolom pada categoryDataGridView. Kami ingin mengatur kolom CategoryId ke baca-saja. Nilai untuk properti CategoryId dihasilkan oleh database setelah kita menyimpan data.

    • Klik kanan kontrol DataGridView dan pilih Edit Kolom...
    • Pilih kolom CategoryId dan atur ReadOnly ke True
    • Tekan OK
  • Pilih Produk dari bawah sumber data Kategori dan seret pada formulir. ProductDataGridView dan productBindingSource ditambahkan ke formulir.

  • Edit kolom pada productDataGridView. Kami ingin menyembunyikan kolom CategoryId dan Category dan mengatur ProductId ke baca-saja. Nilai untuk properti ProductId dihasilkan oleh database setelah kita menyimpan data.

    • Klik kanan kontrol DataGridView dan pilih Edit Kolom....
    • Pilih kolom ProductId dan atur ReadOnly ke True.
    • Pilih kolom CategoryId dan tekan tombol Hapus. Lakukan hal yang sama dengan kolom Kategori .
    • Tekan OK.

    Sejauh ini, kami mengaitkan kontrol DataGridView kami dengan komponen BindingSource dalam perancang. Di bagian berikutnya kita akan menambahkan kode ke kode di belakang untuk mengatur kategoriBindingSource.DataSource ke kumpulan entitas yang saat ini dilacak oleh DbContext. Ketika kami menyeret dan menjatuhkan Produk dari bawah Kategori, WinForms mengurus pengaturan properti productsBindingSource.DataSource ke categoryBindingSource dan productsBindingSource.DataMember properti ke Products. Karena pengikatan ini, hanya produk yang termasuk dalam Kategori yang saat ini dipilih yang akan ditampilkan dalam productDataGridView.

  • Aktifkan tombol Simpan pada toolbar Navigasi dengan mengklik tombol kanan mouse dan memilih Diaktifkan.

    Form 1 Designer

  • Tambahkan penanganan aktivitas untuk tombol simpan dengan mengklik dua kali pada tombol . Ini akan menambahkan penanganan aktivitas dan membawa Anda ke kode di belakang untuk formulir. Kode untuk penanganan aktivitas categoryBindingNavigatorSaveItem_Click akan ditambahkan di bagian berikutnya.

Menambahkan Kode yang Menangani Interaksi Data

Kami sekarang akan menambahkan kode untuk menggunakan ProductContext untuk melakukan akses data. Perbarui kode untuk jendela formulir utama seperti yang ditunjukkan di bawah ini.

Kode mendeklarasikan instans ProductContext yang berjalan lama. Objek ProductContext digunakan untuk mengkueri dan menyimpan data ke database. Metode Dispose() pada instans ProductContext kemudian dipanggil dari metode OnClosing yang ditimpa. Komentar kode memberikan detail tentang apa yang dilakukan kode.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Data.Entity;

    namespace WinFormswithEFSample
    {
        public partial class Form1 : Form
        {
            ProductContext _context;
            public Form1()
            {
                InitializeComponent();
            }

            protected override void OnLoad(EventArgs e)
            {
                base.OnLoad(e);
                _context = new ProductContext();

                // Call the Load method to get the data for the given DbSet
                // from the database.
                // The data is materialized as entities. The entities are managed by
                // the DbContext instance.
                _context.Categories.Load();

                // Bind the categoryBindingSource.DataSource to
                // all the Unchanged, Modified and Added Category objects that
                // are currently tracked by the DbContext.
                // Note that we need to call ToBindingList() on the
                // ObservableCollection<TEntity> returned by
                // the DbSet.Local property to get the BindingList<T>
                // in order to facilitate two-way binding in WinForms.
                this.categoryBindingSource.DataSource =
                    _context.Categories.Local.ToBindingList();
            }

            private void categoryBindingNavigatorSaveItem_Click(object sender, EventArgs e)
            {
                this.Validate();

                // Currently, the Entity Framework doesn’t mark the entities
                // that are removed from a navigation property (in our example the Products)
                // as deleted in the context.
                // The following code uses LINQ to Objects against the Local collection
                // to find all products and marks any that do not have
                // a Category reference as deleted.
                // The ToList call is required because otherwise
                // the collection will be modified
                // by the Remove call while it is being enumerated.
                // In most other situations you can do LINQ to Objects directly
                // against the Local property without using ToList first.
                foreach (var product in _context.Products.Local.ToList())
                {
                    if (product.Category == null)
                    {
                        _context.Products.Remove(product);
                    }
                }

                // Save the changes to the database.
                this._context.SaveChanges();

                // Refresh the controls to show the values         
                // that were generated by the database.
                this.categoryDataGridView.Refresh();
                this.productsDataGridView.Refresh();
            }

            protected override void OnClosing(CancelEventArgs e)
            {
                base.OnClosing(e);
                this._context.Dispose();
            }
        }
    }

Menguji Aplikasi Formulir Windows

  • Kompilasi dan jalankan aplikasi dan Anda dapat menguji fungsionalitasnya.

    Form 1 Before Save

  • Setelah menyimpan kunci yang dihasilkan penyimpanan ditampilkan di layar.

    Form 1 After Save

  • Jika Anda menggunakan Code First, maka Anda juga akan melihat bahwa database WinFormswithEFSample.ProductContext dibuat untuk Anda.

    Server Object Explorer