Bagikan melalui


Kontrol Web Data Berlapis (C#)

oleh Scott Mitchell

Unduh PDF

Dalam tutorial ini kita akan menjelajahi cara menggunakan Repeater yang bersarang di dalam Repeater lainnya. Contoh akan menggambarkan cara mengisi Repeater bagian dalam baik secara deklaratif maupun terprogram.

Pendahuluan

Selain HTML statis dan sintaks pengikatan data, templat juga dapat menyertakan kontrol Web dan Kontrol Pengguna. Kontrol Web ini dapat memiliki properti yang ditetapkan melalui sintaks deklaratif, pengikatan data, atau dapat diakses secara terprogram dalam penanganan aktivitas sisi server yang sesuai.

Dengan menyematkan kontrol dalam templat, tampilan dan pengalaman pengguna dapat disesuaikan dan ditingkatkan. Misalnya, dalam tutorial Menggunakan TemplateFields di GridView Control , kami melihat cara mengkustomisasi tampilan GridView dengan menambahkan kontrol Kalender di TemplateField untuk menunjukkan tanggal sewa karyawan; dalam tutorial Menambahkan Kontrol Validasi ke Antarmuka Pengeditan dan Penyisipan dan Menyesuaikan Antarmuka Modifikasi Data , kami melihat cara menyesuaikan antarmuka pengeditan dan penyisipan dengan menambahkan kontrol validasi, Kotak Teks, DropDownLists, dan kontrol Web lainnya.

Templat juga dapat berisi kontrol Web data lainnya. Artinya, kita dapat memiliki DataList yang berisi DataList lain (atau Repeater atau GridView atau DetailsView, dan sebagainya) di dalam template-nya. Tantangan dengan antarmuka seperti itu adalah mengaitkan data yang sesuai ke kontrol data Web internal. Ada beberapa pendekatan berbeda yang tersedia, mulai dari opsi deklaratif menggunakan ObjectDataSource hingga yang terprogram.

Dalam tutorial ini kita akan menjelajahi cara menggunakan Repeater yang bersarang di dalam Repeater lainnya. Pengulang luar akan berisi item untuk setiap kategori dalam database, menampilkan nama dan deskripsi kategori. Setiap item kategori dalam Repeater akan menampilkan informasi untuk setiap produk milik kategori tersebut (lihat Gambar 1) dalam daftar berpoin. Contoh kami akan menggambarkan cara mengisi Repeater internal baik secara deklaratif maupun dengan pemrograman.

Setiap Kategori, Bersama dengan Produknya, tercantum

Gambar 1: Setiap Kategori, Bersama dengan Produknya, tercantum (Klik untuk melihat gambar ukuran penuh)

Langkah 1: Membuat Daftar Kategori

Saat membuat halaman yang menggunakan kontrol Web data berlapis, saya merasa berguna untuk merancang, membuat, dan menguji kontrol Web data terluar terlebih dahulu, tanpa khawatir tentang kontrol berlapis dalam. Oleh karena itu, mari kita mulai dengan membahas langkah-langkah yang dibutuhkan untuk menambahkan Repeater ke halaman yang mencantumkan nama dan deskripsi untuk setiap kategori.

Mulailah dengan membuka NestedControls.aspx halaman di DataListRepeaterBasics folder dan tambahkan kontrol Pengulang ke halaman, atur propertinya ID ke CategoryList. Dari tag pintar Repeater, pilih untuk membuat ObjectDataSource baru bernama CategoriesDataSource.

Beri nama ObjectDataSource CategoriesDataSource Baru

Gambar 2: Beri nama ObjectDataSource CategoriesDataSource Baru (Klik untuk melihat gambar ukuran penuh)

Konfigurasikan ObjectDataSource sehingga menarik datanya dari CategoriesBLL metode kelas s GetCategories .

Mengonfigurasi ObjectDataSource untuk menggunakan metode GetCategories dari kelas CategoriesBLL

Gambar 3: Konfigurasikan ObjectDataSource untuk Menggunakan Metode Kelas CategoriesBLLGetCategories (Klik untuk melihat gambar ukuran penuh)

Untuk menentukan konten templat Repeater, kita perlu membuka tampilan Sumber dan memasukkan sintaks deklaratif secara manual. ItemTemplate Tambahkan yang menampilkan nama kategori dalam <h4> elemen dan deskripsi kategori dalam elemen paragraf (<p>). Selain itu, mari kita pisahkan setiap kategori dengan aturan horizontal (<hr>). Setelah membuat perubahan ini, halaman Anda harus berisi sintaks deklaratif untuk Repeater dan ObjectDataSource yang mirip dengan berikut ini:

<asp:Repeater ID="CategoryList" DataSourceID="CategoriesDataSource"
    EnableViewState="False" runat="server">
    <ItemTemplate>
        <h4><%# Eval("CategoryName") %></h4>
        <p><%# Eval("Description") %></p>
    </ItemTemplate>
    <SeparatorTemplate>
        <hr />
    </SeparatorTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

Gambar 4 menunjukkan kemajuan kami saat dilihat melalui browser.

Setiap Nama dan Deskripsi Kategori Tercantum, Dipisahkan oleh Aturan Horizontal

Gambar 4: Setiap Nama dan Deskripsi Kategori Tercantum, Dipisahkan oleh Aturan Horizontal (Klik untuk melihat gambar ukuran penuh)

Langkah 2: Menambahkan Pengulang Produk Berlapis

Setelah daftar kategori selesai, tugas kami berikutnya adalah menambahkan Repeater ke CategoryList s ItemTemplate yang nantinya menampilkan informasi tentang produk yang termasuk dalam kategori yang sesuai. Ada sejumlah cara kita dapat mengambil data untuk Repeater dalam ini, dua di antaranya akan segera kita jelajahi. Untuk saat ini, mari kita buat produk Repeater dalam CategoryList Repeater s ItemTemplate. Secara khusus, mari gunakan Repeater untuk menampilkan setiap produk dalam bentuk daftar berpoin, dengan item daftar yang mencakup nama dan harga produk.

Untuk membuat Repeater ini, kita perlu memasukkan sintaks dan templat deklaratif Repeater dalam secara manual ke dalam CategoryListItemTemplate. Tambahkan markup berikut dalam CategoryList Repeater s ItemTemplate:

<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
    runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><strong><%# Eval("ProductName") %></strong>
            (<%# Eval("UnitPrice", "{0:C}") %>)</li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>

Langkah 3: Mengikat Produk Category-Specific ke ProductsByCategoryList Repeater

Jika Anda mengunjungi halaman melalui browser pada saat ini, layar Anda akan terlihat sama seperti pada Gambar 4 karena kami belum mengikat data apa pun ke Repeater. Ada beberapa cara agar kita dapat mengambil catatan produk yang sesuai dan mengikatnya ke Repeater, beberapa lebih efisien daripada yang lain. Tantangan utama di sini adalah mendapatkan kembali produk yang sesuai untuk kategori yang ditentukan.

Data yang akan diikat ke kontrol Repeater dalam dapat diakses secara deklaratif, melalui ObjectDataSource di CategoryList Repeater s ItemTemplate, atau secara terprogram, dari halaman ASP.NET s code-behind. Demikian pula, data ini dapat terikat ke Repeater bagian dalam baik secara deklaratif - melalui properti DataSourceID dari Repeater bagian dalam atau melalui sintaks pengikatan data yang deklaratif maupun secara terprogram dengan merujuk pada Repeater bagian dalam dalam penanganan peristiwa CategoryList dari Repeater, mengatur propertinya secara terprogram dengan ItemDataBound, dan memanggil metodenya DataSource. Mari kita jelajahi masing-masing pendekatan ini.

Mengakses Data Secara Deklaratif dengan Kontrol ObjectDataSource danItemDataBoundPenanganan Aktivitas

Karena kami telah menggunakan ObjectDataSource secara luas di seluruh seri tutorial ini, pilihan paling alami untuk mengakses data untuk contoh ini adalah tetap dengan ObjectDataSource. Kelas ProductsBLL memiliki metode GetProductsByCategoryID(categoryID) yang mengembalikan informasi tentang produk-produk yang termasuk dalam categoryID. Oleh karena itu, kita dapat menambahkan ObjectDataSource ke CategoryList Repeater s ItemTemplate dan mengonfigurasinya untuk mengakses datanya dari metode kelas ini.

Sayangnya, Repeater tidak mengizinkan templatnya diedit melalui tampilan Desain sehingga kita perlu menambahkan sintaks deklaratif untuk kontrol ObjectDataSource ini dengan tangan. Sintaks berikut menunjukkan CategoryList Repeater s ItemTemplate setelah menambahkan ObjectDataSource baru ini (ProductsByCategoryDataSource):

<h4><%# Eval("CategoryName") %></h4>
<p><%# Eval("Description") %></p>
<asp:Repeater ID="ProductsByCategoryList" EnableViewState="False"
        DataSourceID="ProductsByCategoryDataSource" runat="server">
    <HeaderTemplate>
        <ul>
    </HeaderTemplate>
    <ItemTemplate>
        <li><strong><%# Eval("ProductName") %></strong> -
                sold as <%# Eval("QuantityPerUnit") %> at
                <%# Eval("UnitPrice", "{0:C}") %></li>
    </ItemTemplate>
    <FooterTemplate>
        </ul>
    </FooterTemplate>
</asp:Repeater>
<asp:ObjectDataSource ID="ProductsByCategoryDataSource" runat="server"
           SelectMethod="GetProductsByCategoryID" TypeName="ProductsBLL">
   <SelectParameters>
        <asp:Parameter Name="CategoryID" Type="Int32" />
   </SelectParameters>
</asp:ObjectDataSource>

Saat menggunakan pendekatan ObjectDataSource, kita perlu mengatur properti ProductsByCategoryList dari Repeater ke DataSourceID milik ObjectDataSource (ID). Selain itu, perhatikan bahwa ObjectDataSource kami memiliki elemen <asp:Parameter> yang menetapkan nilai categoryID untuk diteruskan ke metode GetProductsByCategoryID(categoryID). Tetapi bagaimana kita menentukan nilai ini? Idealnya, kita akan dapat mengatur DefaultValue properti <asp:Parameter> elemen menggunakan sintaks pengikatan data, seperti:

<asp:Parameter Name="CategoryID" Type="Int32"
     DefaultValue='<%# Eval("CategoryID")' />

Sayangnya, sintaks pengikatan data hanya valid dalam kontrol yang memiliki DataBinding event. Kelas Parameter tidak memiliki peristiwa seperti itu dan oleh karena itu sintaks di atas ilegal dan akan mengakibatkan kesalahan runtime.

Untuk mengatur nilai ini, kita perlu membuat penanganan aktivitas untuk CategoryList peristiwa Repeater.ItemDataBound Ingat bahwa peristiwa ItemDataBound dipicu sekali untuk setiap item yang terikat ke Repeater. Oleh karena itu, setiap kali peristiwa ini diaktifkan untuk Repeater terluar, kita dapat menetapkan nilai saat ini CategoryID ke parameter ProductsByCategoryDataSource pada CategoryID ObjectDataSource.

Buat penanganan aktivitas untuk CategoryList peristiwa Repeater ItemDataBound dengan kode berikut:

protected void CategoryList_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.AlternatingItem ||
        e.Item.ItemType == ListItemType.Item)
    {
        // Reference the CategoriesRow object being bound to this RepeaterItem
        Northwind.CategoriesRow category =
            (Northwind.CategoriesRow)((System.Data.DataRowView)e.Item.DataItem).Row;
        // Reference the ProductsByCategoryDataSource ObjectDataSource
        ObjectDataSource ProductsByCategoryDataSource =
            (ObjectDataSource)e.Item.FindControl("ProductsByCategoryDataSource");
        // Set the CategoryID Parameter value
        ProductsByCategoryDataSource.SelectParameters["CategoryID"].DefaultValue =
            category.CategoryID.ToString();
    }
}

Penanganan aktivitas ini dimulai dengan memastikan bahwa kita berurusan dengan item data daripada item header, footer, atau pemisah. Selanjutnya, kita merujuk instans aktual CategoriesRow yang baru saja terikat ke RepeaterItem yang kini aktif. Terakhir, kami mereferensikan ObjectDataSource dalam ItemTemplate dan menetapkan nilai parameternya CategoryID ke CategoryID dari RepeaterItem saat ini.

Dengan penanganan peristiwa ini, ProductsByCategoryList Repeater di setiap RepeaterItem terhubung dengan produk dalam kategori RepeaterItem. Gambar 5 menunjukkan cuplikan layar dari output yang dihasilkan.

Pengulang Luar Mencantumkan Setiap Kategori; Yang Dalam Mencantumkan Produk untuk Kategori tersebut

Gambar 5: Pengulang Luar Mencantumkan Setiap Kategori; Inner One Mencantumkan Produk untuk Kategori tersebut (Klik untuk melihat gambar ukuran penuh)

Mengakses Data Produk menurut Kategori Secara Terprogram

Alih-alih menggunakan ObjectDataSource untuk mengambil produk untuk kategori saat ini, kita dapat mengembangkan metode di kelas code-behind halaman ASP.NET kami (atau di dalam folder App_Code atau dalam proyek Pustaka Kelas terpisah) yang mengembalikan produk yang tepat ketika ditentukan dalam CategoryID. Bayangkan bahwa kita memiliki metode seperti itu di halaman ASP.NET kita pada kelas code-behind dan diberi nama GetProductsInCategory(categoryID). Dengan metode ini, kita dapat mengikat produk untuk kategori saat ini ke Repeater bagian dalam menggunakan sintaks deklaratif berikut:

<asp:Repeater runat="server" ID="ProductsByCategoryList" EnableViewState="False"
      DataSource='<%# GetProductsInCategory((int)(Eval("CategoryID"))) %>'>
  ...
</asp:Repeater>

Properti Repeater s DataSource menggunakan sintaks pengikatan data untuk menunjukkan bahwa datanya berasal dari GetProductsInCategory(categoryID) metode . Karena Eval("CategoryID") mengembalikan nilai tipe Object, kita mengubah objek menjadi Integer terlebih dahulu sebelum meneruskannya ke metode GetProductsInCategory(categoryID). Perhatikan bahwa CategoryID yang diakses di sini melalui sintaks pengikatan data adalah CategoryID dalam Repeater luar (CategoryList), yang terikat ke rekaman pada tabel Categories. Oleh karena itu, kita tahu bahwa CategoryID tidak dapat menjadi nilai database NULL , itulah sebabnya kita dapat melemparkan Eval metode secara membabi buta tanpa memeriksa apakah kita berurusan dengan DBNull.

Dengan pendekatan ini, kita perlu membuat metode GetProductsInCategory(categoryID) dan menggunakannya untuk mengambil set produk yang sesuai yang diberikan oleh categoryID. Kita dapat melakukan ini dengan mengembalikan ProductsDataTable yang dikembalikan oleh metode ProductsBLL dari kelas GetProductsByCategoryID(categoryID). Mari kita buat metode GetProductsInCategory(categoryID) di kelas code-behind untuk NestedControls.aspx halaman. Lakukan menggunakan kode berikut:

protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID)
{
    // Create an instance of the ProductsBLL class
    ProductsBLL productAPI = new ProductsBLL();
    // Return the products in the category
    return productAPI.GetProductsByCategoryID(categoryID);
}

Metode ini hanya membuat instans ProductsBLL metode dan mengembalikan hasil GetProductsByCategoryID(categoryID) metode . Perhatikan bahwa metode harus ditandai Public atau Protected; jika metode ditandai Private, metode tidak akan dapat diakses dari markup deklaratif halaman ASP.NET.

Setelah membuat perubahan ini untuk menggunakan teknik baru ini, luangkan waktu sejenak untuk melihat halaman melalui browser. Output harus identik dengan output saat menggunakan ObjectDataSource dan ItemDataBound pendekatan penanganan aktivitas (lihat kembali ke Gambar 5 untuk melihat cuplikan layar).

Nota

Mungkin tampak seperti pekerjaan yang sibuk untuk membuat metode GetProductsInCategory(categoryID) di kelas code-behind halaman ASP.NET. Bagaimanapun, metode ini hanya membuat instans kelas ProductsBLL dan mengembalikan hasil dari metode GetProductsByCategoryID(categoryID). Mengapa tidak hanya memanggil metode ini langsung dari sintaks pengikatan data di Repeater bagian dalam, seperti: DataSource='<%# ProductsBLL.GetProductsByCategoryID((int)(Eval("CategoryID"))) %>'? Meskipun sintaks ini tidak akan berfungsi dengan implementasi ProductsBLL kelas kami saat ini (karena GetProductsByCategoryID(categoryID) metode ini adalah metode instans), Anda dapat memodifikasi ProductsBLL untuk menyertakan metode statis GetProductsByCategoryID(categoryID) atau meminta kelas menyertakan metode statis Instance() untuk mengembalikan instans ProductsBLL baru kelas.

Meskipun modifikasi tersebut akan menghilangkan kebutuhan akan metode GetProductsInCategory(categoryID) di kelas code-behind halaman ASP.NET, metode di kelas code-behind tersebut memberikan kita lebih banyak fleksibilitas dalam bekerja dengan data yang diambil, seperti yang akan kita lihat sebentar lagi.

Mengakses Semua Informasi Produk Sekaligus

Dua teknik sebelumnya yang telah kami periksa mengambil produk tersebut untuk kategori saat ini dengan melakukan panggilan ke kelas ProductsBLL metode GetProductsByCategoryID(categoryID) (pendekatan pertama melakukannya melalui ObjectDataSource, yang kedua melalui metode GetProductsInCategory(categoryID) di kelas code-behind). Setiap kali metode ini dipanggil, Lapisan Logika Bisnis memanggil Lapisan Akses Data, yang melakukan kueri SQL ke database untuk mengembalikan baris dari tabel Products yang nilai bidang CategoryID-nya cocok dengan parameter input yang disediakan.

Mengingat adanya N kategori dalam sistem, pendekatan ini menghasilkan N + 1 panggilan ke database. Satu kueri database dilakukan untuk mendapatkan semua kategori, dan kemudian N panggilan untuk mendapatkan produk yang spesifik untuk setiap kategori. Namun, kita dapat mengambil semua data yang diperlukan hanya dalam dua panggilan database satu panggilan untuk mendapatkan semua kategori dan yang lain untuk mendapatkan semua produk. Setelah kita memiliki semua produk, kita dapat memfilter produk-produk tersebut sehingga hanya produk yang sesuai dengan CategoryID yang sedang aktif yang terhubung dengan kategori tersebut dalam Repeater-nya.

Untuk menyediakan fungsionalitas ini, kita hanya perlu melakukan sedikit modifikasi pada GetProductsInCategory(categoryID) metode di kelas code-behind halaman ASP.NET kami. Daripada mengembalikan hasil dari metode kelas ProductsBLLGetProductsByCategoryID(categoryID) secara membabi buta, kita dapat terlebih dahulu mengakses semua produk (jika belum diakses sebelumnya) dan kemudian hanya mengembalikan tampilan produk yang sudah difilter berdasarkan parameter yang diteruskan CategoryID.

private Northwind.ProductsDataTable allProducts = null;
protected Northwind.ProductsDataTable GetProductsInCategory(int categoryID)
{
    // First, see if we've yet to have accessed all of the product information
    if (allProducts == null)
    {
        ProductsBLL productAPI = new ProductsBLL();
        allProducts = productAPI.GetProducts();
    }
    // Return the filtered view
    allProducts.DefaultView.RowFilter = "CategoryID = " + categoryID;
    return allProducts;
}

Perhatikan penambahan variabel tingkat halaman, allProducts. Ini menyimpan informasi tentang semua produk dan akan terisi ketika pertama kali metode GetProductsInCategory(categoryID) dipanggil. Setelah memastikan bahwa allProducts objek telah dibuat dan diisi, metode memfilter hasil DataTable s s sehingga hanya baris CategoryID yang cocok dengan yang ditentukan CategoryID yang dapat diakses. Pendekatan ini mengurangi berapa kali database diakses dari N + 1 menjadi dua.

Peningkatan ini tidak memperkenalkan perubahan apa pun pada markup halaman yang dirender, juga tidak mengembalikan lebih sedikit rekaman daripada pendekatan lainnya. Ini hanya mengurangi jumlah panggilan ke database.

Nota

Orang mungkin berpikir secara intuitif bahwa mengurangi jumlah akses ke database tentu saja akan meningkatkan kinerja. Namun, ini mungkin tidak terjadi. Jika Anda memiliki sejumlah besar produk yang memiliki CategoryID sebagai NULL, misalnya, panggilan metode GetProducts mengembalikan sejumlah produk yang tidak pernah ditampilkan. Selain itu, mengembalikan semua produk bisa menjadi pemborosan jika Anda hanya menampilkan subset dari kategori, yang mungkin terjadi jika Anda telah menerapkan pemanggilan halaman.

Seperti biasa, dalam hal menganalisis performa dua teknik, satu-satunya ukuran surefire adalah menjalankan pengujian terkontrol yang disesuaikan untuk skenario kasus umum aplikasi Anda.

Ringkasan

Dalam tutorial ini kita melihat cara menempatkan satu kontrol data Web di dalam kontrol lainnya, secara khusus memeriksa bagaimana Pengulang luar menampilkan item untuk setiap kategori dengan Pengulang dalam yang mencantumkan produk untuk setiap kategori dalam daftar berbentuk simbol atau poin. Tantangan utama dalam membangun antarmuka pengguna berlapis terletak pada mengakses dan mengikat data yang benar ke kontrol Web data dalam. Ada berbagai teknik yang tersedia, dua di antaranya kami periksa dalam tutorial ini. Pendekatan pertama yang diperiksa menggunakan ObjectDataSource di kontrol data Web luar yang terikat ke kontrol data Web dalam melalui propertinya. Teknik kedua mengakses data melalui metode di kelas code-behind halaman ASP.NET. Metode ini kemudian dapat terikat ke properti kontrol Web data internal DataSource melalui sintaks pembindungan data.

Meskipun antarmuka pengguna tertanam yang dibahas dalam tutorial ini menggunakan Repeater yang tertanam dalam Repeater, teknik ini dapat diperluas ke kontrol web data lainnya. Anda dapat memasukkan Repeater ke dalam GridView, atau GridView ke dalam DataList, dan sebagainya.

Selamat Pemrograman!

Tentang Penulis

Scott Mitchell, penulis tujuh buku ASP/ASP.NET dan pendiri 4GuysFromRolla.com, telah bekerja sama dengan teknologi Microsoft Web sejak 1998. Scott bekerja sebagai konsultan, pelatih, dan penulis independen. Buku terbarunya adalah Sams Teach Yourself ASP.NET 2.0 dalam 24 Jam. Dia dapat dijangkau di mitchell@4GuysFromRolla.com.

Ucapan terima kasih khusus kepada

Seri tutorial ini ditinjau oleh banyak peninjau yang bermanfaat. Peninjau utama untuk tutorial ini adalah Zack Jones dan Liz Shulok. Tertarik untuk meninjau artikel MSDN saya yang akan datang? Jika demikian, hubungi saya di mitchell@4GuysFromRolla.com.