Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
oleh Microsoft
Ini adalah langkah 8 dari tutorial aplikasi "NerdDinner" gratis yang memandu cara membangun aplikasi web kecil, tetapi lengkap menggunakan ASP.NET MVC 1.
Langkah 8 menunjukkan cara menambahkan dukungan paging ke URL /Dinners kami sehingga alih-alih menampilkan 1000-an makan malam sekaligus, kami hanya akan menampilkan 10 makan malam mendatang sekaligus - dan memungkinkan pengguna akhir untuk melakukan page back dan forward melalui seluruh daftar dengan cara yang ramah SEO.
Jika Anda menggunakan ASP.NET MVC 3, kami sarankan Anda mengikuti tutorial Memulai MVC 3 atau MVC Music Store .
NerdDinner Langkah 8: Dukungan Halaman
Jika situs kami berhasil, itu akan memiliki ribuan makan malam yang akan datang. Kita perlu memastikan bahwa UI kita menskalakan untuk menangani semua makan malam ini, dan memungkinkan pengguna untuk menelusurinya. Untuk mengaktifkan ini, kami akan menambahkan dukungan paging ke URL /Dinners kami sehingga alih-alih menampilkan 1000-an makan malam sekaligus, kami hanya akan menampilkan 10 makan malam mendatang sekaligus - dan memungkinkan pengguna akhir untuk melakukan page back dan forward melalui seluruh daftar dengan cara yang ramah SEO.
Rekap Metode Tindakan Index()
Metode tindakan Index() dalam kelas DinnersController kami saat ini terlihat seperti di bawah ini:
//
// GET: /Dinners/
public ActionResult Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
return View(dinners);
}
Ketika permintaan dibuat ke URL /Dinners , permintaan mengambil daftar semua makan malam yang akan datang dan kemudian merender daftar semuanya:
Memahami IQueryable<T>
Iqueryable<T> adalah antarmuka yang diperkenalkan dengan LINQ sebagai bagian dari .NET 3.5. Ini memungkinkan skenario "eksekusi yang ditangguhkan" yang kuat yang dapat kita manfaatkan untuk mengimplementasikan dukungan halaman.
Dalam DinnerRepository kami, kami mengembalikan urutan IQueryable<Dinner> dari metode FindUpcomingDinners() kami:
public class DinnerRepository {
private NerdDinnerDataContext db = new NerdDinnerDataContext();
//
// Query Methods
public IQueryable<Dinner> FindUpcomingDinners() {
return from dinner in db.Dinners
where dinner.EventDate > DateTime.Now
orderby dinner.EventDate
select dinner;
}
Objek IQueryable Dinner> yang<dikembalikan oleh metode FindUpcomingDinners() kami merangkum kueri untuk mengambil objek Makan Malam dari database kami menggunakan LINQ ke SQL. Yang penting, kueri tidak akan dijalankan terhadap database sampai kami mencoba mengakses/melakukan iterasi atas data dalam kueri, atau sampai kami memanggil metode ToList() di dalamnya. Kode yang memanggil metode FindUpcomingDinners() kami dapat secara opsional memilih untuk menambahkan operasi/filter "berantai" tambahan ke objek IQueryable<Dinner> sebelum menjalankan kueri. LINQ ke SQL kemudian cukup cerdas untuk menjalankan kueri gabungan terhadap database saat data diminta.
Untuk menerapkan logika halaman, kita dapat memperbarui metode tindakan DinnersController's Index() sehingga menerapkan operator "Lewati" dan "Ambil" tambahan ke urutan IQueryable Dinner> yang<dikembalikan sebelum memanggil ToList() di dalamnya:
//
// GET: /Dinners/
public ActionResult Index() {
var upcomingDinners = dinnerRepository.FindUpcomingDinners();
var paginatedDinners = upcomingDinners.Skip(10).Take(20).ToList();
return View(paginatedDinners);
}
Kode di atas melewati 10 makan malam pertama yang akan datang dalam database, dan kemudian mengembalikan 20 makan malam. LINQ ke SQL cukup cerdas untuk membuat kueri SQL yang dioptimalkan yang melakukan logika melompati ini di database SQL - dan bukan di server web. Ini berarti bahwa bahkan jika kita memiliki jutaan Makan Malam yang akan datang dalam database, hanya 10 yang kita inginkan yang akan diambil sebagai bagian dari permintaan ini (membuatnya efisien dan dapat diskalakan).
Menambahkan nilai "halaman" ke URL
Alih-alih mengkodekan rentang halaman tertentu, kita akan ingin URL kita menyertakan parameter "halaman" yang menunjukkan rentang Makan Malam mana yang diminta pengguna.
Menggunakan nilai Querystring
Kode di bawah ini menunjukkan bagaimana kami dapat memperbarui metode tindakan Index() kami untuk mendukung parameter querystring dan mengaktifkan URL seperti /Dinners?page=2:
//
// GET: /Dinners/
// /Dinners?page=2
public ActionResult Index(int? page) {
const int pageSize = 10;
var upcomingDinners = dinnerRepository.FindUpcomingDinners();
var paginatedDinners = upcomingDinners.Skip((page ?? 0) * pageSize)
.Take(pageSize)
.ToList();
return View(paginatedDinners);
}
Metode tindakan Index() di atas memiliki parameter bernama "page". Parameter dinyatakan sebagai bilangan bulat null (itulah yang ditunjukkan oleh int? ). Ini berarti bahwa URL /Dinners?page=2 akan menyebabkan nilai "2" diteruskan sebagai nilai parameter. URL /Dinners (tanpa nilai querystring) akan menyebabkan nilai null diteruskan.
Kami mengalikan nilai halaman dengan ukuran halaman (dalam hal ini 10 baris) untuk menentukan berapa banyak makan malam yang akan dilewati. Kami menggunakan operator "coalescing" null C# (??) yang berguna saat berhadapan dengan jenis nullable. Kode di atas menetapkan halaman nilai 0 jika parameter halaman null.
Menggunakan nilai URL Tersemat
Alternatif untuk menggunakan nilai querystring adalah menyematkan parameter halaman dalam URL aktual itu sendiri. Misalnya: /Dinners/Page/2 atau /Dinners/2. ASP.NET MVC menyertakan mesin perutean URL yang kuat yang memudahkan untuk mendukung skenario seperti ini.
Kita dapat mendaftarkan aturan perutean kustom yang memetakan format URL atau URL masuk ke kelas pengontrol atau metode tindakan apa pun yang kita inginkan. Yang perlu kita lakukan adalah membuka file Global.asax dalam proyek kita:
Lalu daftarkan aturan pemetaan baru menggunakan metode pembantu MapRoute() seperti panggilan pertama ke rute. MapRoute() di bawah ini:
public void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"UpcomingDinners", // Route name
"Dinners/Page/{page}", // URL with params
new { controller = "Dinners", action = "Index" } // Param defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with params
new { controller="Home", action="Index",id="" } // Param defaults
);
}
void Application_Start() {
RegisterRoutes(RouteTable.Routes);
}
Di atas kami mendaftarkan aturan perutean baru bernama "UpcomingDinners". Kami menunjukkan ia memiliki format URL "Dinners/Page/{page}" – di mana {page} adalah nilai parameter yang disematkan dalam URL. Parameter ketiga untuk metode MapRoute() menunjukkan bahwa kita harus memetakan URL yang cocok dengan format ini dengan metode tindakan Index() pada kelas DinnersController.
Kita dapat menggunakan kode Index() yang sama persis dengan skenario Querystring kita - kecuali sekarang parameter "halaman" kita akan berasal dari URL dan bukan querystring:
//
// GET: /Dinners/
// /Dinners/Page/2
public ActionResult Index(int? page) {
const int pageSize = 10;
var upcomingDinners = dinnerRepository.FindUpcomingDinners();
var paginatedDinners = upcomingDinners.Skip((page ?? 0) * pageSize)
.Take(pageSize)
.ToList();
return View(paginatedDinners);
}
Dan sekarang ketika kita menjalankan aplikasi dan mengetik /Dinners kita akan melihat 10 makan malam pertama yang akan datang:
Dan ketika kita mengetik /Dinners/Page/1 kita akan melihat halaman makan malam berikutnya:
Menambahkan antarmuka pengguna navigasi halaman
Langkah terakhir untuk menyelesaikan skenario halaman kami adalah menerapkan antarmuka pengguna navigasi "berikutnya" dan "sebelumnya" dalam templat tampilan kami untuk memungkinkan pengguna melompati data Makan Malam dengan mudah.
Untuk menerapkan ini dengan benar, kita perlu mengetahui jumlah total Makan Malam dalam database, serta berapa banyak halaman data yang diterjemahkan ini. Kita kemudian perlu menghitung apakah nilai "halaman" yang saat ini diminta berada di awal atau akhir data, dan menampilkan atau menyembunyikan UI "sebelumnya" dan "berikutnya" yang sesuai. Kita dapat menerapkan logika ini dalam metode tindakan Index() kita. Atau kita dapat menambahkan kelas pembantu ke proyek kita yang merangkum logika ini dengan cara yang lebih dapat digunakan kembali.
Di bawah ini adalah kelas pembantu "PaginatedList" sederhana yang berasal dari kelas koleksi List<T> bawaan .NET Framework. Ini mengimplementasikan kelas pengumpulan yang dapat digunakan kembali yang dapat digunakan untuk mem-paginate urutan data IQueryable apa pun. Dalam aplikasi NerdDinner kami, kami akan memilikinya bekerja atas hasil IQueryable<Dinner>, tetapi dapat dengan mudah digunakan terhadap Produk> IQueryable<atau hasil Pelanggan> yang Dapat<Dikueri dalam skenario aplikasi lain:
public class PaginatedList<T> : List<T> {
public int PageIndex { get; private set; }
public int PageSize { get; private set; }
public int TotalCount { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize) {
PageIndex = pageIndex;
PageSize = pageSize;
TotalCount = source.Count();
TotalPages = (int) Math.Ceiling(TotalCount / (double)PageSize);
this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
}
public bool HasPreviousPage {
get {
return (PageIndex > 0);
}
}
public bool HasNextPage {
get {
return (PageIndex+1 < TotalPages);
}
}
}
Perhatikan di atas cara menghitung dan kemudian mengekspos properti seperti "PageIndex", "PageSize", "TotalCount", dan "TotalPages". Ini juga kemudian mengekspos dua properti pembantu "HasPreviousPage" dan "HasNextPage" yang menunjukkan apakah halaman data dalam koleksi berada di awal atau akhir urutan asli. Kode di atas akan menyebabkan dua kueri SQL dijalankan - yang pertama mengambil jumlah total objek Makan Malam (ini tidak mengembalikan objek - melainkan melakukan pernyataan "SELECT COUNT" yang mengembalikan bilangan bulat), dan yang kedua hanya mengambil baris data yang kita butuhkan dari database kita untuk halaman data saat ini.
Kami kemudian dapat memperbarui metode pembantu DinnersController.Index() kami untuk membuat Makan Malam> PaginatedList<dari hasil DinnerRepository.FindUpcomingDinners(), dan meneruskannya ke templat tampilan kami:
//
// GET: /Dinners/
// /Dinners/Page/2
public ActionResult Index(int? page) {
const int pageSize = 10;
var upcomingDinners = dinnerRepository.FindUpcomingDinners();
var paginatedDinners = new PaginatedList<Dinner>(upcomingDinners, page ?? 0, pageSize);
return View(paginatedDinners);
}
Kami kemudian dapat memperbarui templat tampilan \Views\Dinners\Index.aspx untuk mewarisi dari ViewPage<NerdDinner.Helpers.PaginatedList<Dinner>> alih-alih ViewPage<IEnumerable<Dinner>>, lalu menambahkan kode berikut ke bagian bawah view-template kami untuk menampilkan atau menyembunyikan antarmuka pengguna navigasi berikutnya dan sebelumnya:
<% if (Model.HasPreviousPage) { %>
<%= Html.RouteLink("<<<", "UpcomingDinners", new { page = (Model.PageIndex-1) }) %>
<% } %>
<% if (Model.HasNextPage) { %>
<%= Html.RouteLink(">>>", "UpcomingDinners", new { page = (Model.PageIndex + 1) }) %>
<% } %>
Perhatikan di atas bagaimana kami menggunakan metode pembantu Html.RouteLink() untuk menghasilkan hyperlink kami. Metode ini mirip dengan metode pembantu Html.ActionLink() yang telah kami gunakan sebelumnya. Perbedaannya adalah kami menghasilkan URL menggunakan aturan perutean "UpcomingDinners" yang kami siapkan dalam file Global.asax kami. Ini memastikan bahwa kami akan menghasilkan URL ke metode tindakan Index() kami yang memiliki format: /Dinners/Page/{page} – di mana nilai {page} adalah variabel yang kami sediakan di atas berdasarkan PageIndex saat ini.
Dan sekarang ketika kita menjalankan aplikasi lagi kita akan melihat 10 makan malam pada satu waktu di browser kita:
Kami juga memiliki <<< antarmuka pengguna navigasi dan >>> di bagian bawah halaman yang memungkinkan kami untuk melewati maju dan mundur melalui data kami menggunakan URL yang dapat diakses mesin pencari:
| Topik Samping: Memahami implikasi IQueryable<T> |
|---|
| IQueryable<T> adalah fitur yang sangat kuat yang memungkinkan berbagai skenario eksekusi yang ditangguhkan yang menarik (seperti kueri berbasis penomoran dan komposisi). Seperti semua fitur canggih, Anda ingin berhati-hati dengan cara Anda menggunakannya dan memastikannya tidak disalahgunakan. Penting untuk mengenali bahwa mengembalikan hasil IQueryable<T> dari repositori Anda memungkinkan kode panggilan untuk ditambahkan pada metode operator berantai ke dalamnya, sehingga berpartisipasi dalam eksekusi kueri utama. Jika Anda tidak ingin memberikan kode panggilan kemampuan ini, maka Anda harus mengembalikan hasil IList<T> atau IEnumerable<T> - yang berisi hasil kueri yang telah dijalankan. Untuk skenario penomoran halaman ini akan mengharuskan Anda untuk mendorong logika penomoran halaman data aktual ke dalam metode repositori yang dipanggil. Dalam skenario ini, kami mungkin memperbarui metode pencari FindUpcomingDinners() kami untuk memiliki tanda tangan yang mengembalikan PaginatedList: PaginatedList< Dinner> FindUpcomingDinners(int pageIndex, int pageSize) { } Atau kembalikan IList<Dinner>, dan gunakan param "totalCount" untuk mengembalikan jumlah total Makan Malam: IList<Dinner> FindUpcomingDinners(int pageIndex, int pageSize, out int totalCount) { } |
Langkah Selanjutnya
Sekarang mari kita lihat bagaimana kita dapat menambahkan dukungan autentikasi dan otorisasi ke aplikasi kita.