hdth09 ltudql02-linq-e3

10
Lập trình ứng dụng quản lý 2 Tìm kiếm & phân trang với LINQ to SQL Ngô Ngọc Đăng Khoa 2011, April 26

Upload: dung-dinh

Post on 09-Jul-2015

361 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Hdth09 ltudql02-linq-e3

Lập trình ứng dụng quản lý 2

Tìm kiếm & phân trang với LINQ to SQL

Ngô Ngọc Đăng Khoa

2011, April 26

Page 2: Hdth09 ltudql02-linq-e3

1 LTUDQL2 – TUT05

1 Mục tiêu & Yêu cầu

1.1 Mục tiêu

Nắm được phương pháp xây dựng chức năng tìm kiếm & phân trang

trong ứng dụng LINQ to SQL với VB.NET

1.2 Yêu cầu

Sinh viên đã hoàn thành tốt 2 tutorial LINQ trước

Đọc kỹ & thực hiện các bước theo tutorial

2 Bài tập áp dụng

2.1 Yêu cầu

Sử dụng CSDL Northwind, xây dựng chức năng tìm kiếm các sản phẩm đang

có trong CSDL theo các tiêu chí sau:

Tên sản phẩm

Danh mục

Nhà cung cấp

Giá

Kết quả tìm kiếm phải được phân trang (1 trang 10 kết quả) & có dàn nút

điều hướng trang đầy đủ.

Page 3: Hdth09 ltudql02-linq-e3

2 LTUDQL2 – TUT05

2.2 Hướng dẫn

(Sinh viên sử dụng script CSDL Northwind ở tuần 7)

Bước 1: Add LINQ to SQL class ứng với CSDL Northwind vào project

Bước 2: thiết kế giao diện frmMain như hình dưới

Bước 3: khai báo 1 context toàn cục & đổ dữ liệu vào 2 comboBox

Dim ctx As New NorthwindDataContext Private Sub frmMain_Load(...) Handles MyBase.Load Dim listCat As List(Of Category) = ctx.Categories.ToList() listCat.Insert(0, New Category With {.CategoryID = -1, .CategoryName = "ALL"}) cboCat.DataSource = listCat cboCat.DisplayMember = "CategoryName" cboCat.ValueMember = "CategoryID" Dim listSupp As List(Of Supplier) = ctx.Suppliers.ToList() listSupp.Insert(0, New Supplier With {.SupplierID = -1, .CompanyName = "ALL"}) cboSupp.DataSource = listSupp cboSupp.DisplayMember = "CompanyName" cboSupp.ValueMember = "SupplierID" End Sub

Page 4: Hdth09 ltudql02-linq-e3

3 LTUDQL2 – TUT05

Bước 4: xây dựng hàm tạo câu truy vấn dựa vào dữ liệu nhập trên các controls

Function BuildSearchQuery() As IQueryable(Of Product) Dim query As IQueryable(Of Product) = ctx.Products If txtProductName.Text.Length > 0 Then query = query.Where( _ Function(p) p.ProductName.Contains(txtProductName.Text) _ ) End If Dim catId As Integer = cboCat.SelectedValue If catId > -1 Then query = query.Where(Function(p) p.CategoryID = catId) End If Dim suppId As Integer = cboSupp.SelectedValue If suppId > -1 Then query = query.Where(Function(p) p.SupplierID = suppId) End If Dim priceF As Integer = txtPriceF.Value If priceF > 0 Then query = query.Where(Function(p) p.UnitPrice >= priceF) End If Dim priceT As Integer = txtPriceT.Value If priceT > 0 Then query = query.Where(Function(p) p.UnitPrice <= priceT) End If Return query End Function

Bước 5: xây dựng hàm thực hiện chức năng tìm kiếm

Private Sub btnSearch_Click(...) Handles btnSearch.Click Dim query As IQueryable(Of Product) = BuildSearchQuery() grid.DataSource = query End Sub

Bước 6: bổ sung các tham số dùng cho việc chuyển trang (các biến toàn cục)

Dim ctx As New NorthwindDataContext Const PAGE_SIZE As Integer = 10 Dim curPage As Integer Dim numberOfPages As Integer

Page 5: Hdth09 ltudql02-linq-e3

4 LTUDQL2 – TUT05

Bước 7: xây dựng hàm điều khiển trạng thái của các nút điều hướng trang

Sub SetNavigationState() btnNext.Enabled = (curPage < numberOfPages) btnLast.Enabled = (curPage < numberOfPages) btnPrev.Enabled = (curPage > 1) btnFirst.Enabled = (curPage > 1) End Sub

Bước 8: bổ sung lời gọi hàm vào hàm frmMain_Load

Private Sub frmMain_Load(...) Handles MyBase.Load Dim listCat As List(Of Category) = ctx.Categories.ToList() listCat.Insert(0, New Category With {.CategoryID = -1, .CategoryName = "ALL"}) cboCat.DataSource = listCat cboCat.DisplayMember = "CategoryName" cboCat.ValueMember = "CategoryID" Dim listSupp As List(Of Supplier) = ctx.Suppliers.ToList() listSupp.Insert(0, New Supplier With {.SupplierID = -1, .CompanyName = "ALL"}) cboSupp.DataSource = listSupp cboSupp.DisplayMember = "CompanyName" cboSupp.ValueMember = "SupplierID" lbPaging.Text = "0/0" SetNavigationState() End Sub

Bước 9: sửa hàm btnSearch_Click để ứng dụng lấy dữ liệu đúng trang cần thiết

& tính toán các tham số cho việc phân trang

Private Sub btnSearch_Click(...) Handles btnSearch.Click Dim query As IQueryable(Of Product) = BuildSearchQuery() numberOfPages = Math.Ceiling(query.Count() / PAGE_SIZE) curPage = 1 lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = query.Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub

Page 6: Hdth09 ltudql02-linq-e3

5 LTUDQL2 – TUT05

Bước 10: xây dựng các hàm điều hướng trang, cuối mỗi hàm đều cần gọi lại hàm

SetNavigationState nhằm chỉnh lại tình trạng của dàn nút điều hướng

Private Sub btnNext_Click(...) Handles btnNext.Click curPage += 1 lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = BuildSearchQuery().Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub Private Sub btnPrev_Click(...) Handles btnPrev.Click curPage -= 1 lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = BuildSearchQuery().Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub Private Sub btnLast_Click(...) Handles btnLast.Click curPage = numberOfPages lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = BuildSearchQuery().Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub Private Sub btnFirst_Click(...) Handles btnFirst.Click curPage = 1 lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = BuildSearchQuery().Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub

Nhận xét: trong các hàm điều hướng, ta nhận thấy đều có lời gọi hàm

BuildSearchQuery nhằm tạo lại câu truy vấn, điều đó dẫn đến hệ quả người

dùng có thể thay đổi điều kiện tìm kiếm & bấm vào dàn nút điều hướng để xem

kết quả truy vấn mới, việc này trong một số trường hợp sẽ dẫn đến lỗi (vd: kết quả

truy vấn cũ có 4 trang & người dùng đang xem trang 3, người dùng sau đó thay đổi điều

kiện truy vấn và nhấn Next. Tuy nhiên, kết quả truy vấn mới chỉ có 2 trang & gây ra lỗi).

Để giải quyết tình trạng trên, ta cần bổ sung 1 số thành phần nhằm đảm bảo

người dùng chỉ có thể chuyển trang sau khi bấm Search, nếu người dùng thay

đổi điều kiện tìm kiếm thì họ phải bấm Search & xem kết quả từ trang 1.

Page 7: Hdth09 ltudql02-linq-e3

6 LTUDQL2 – TUT05

Bước 11: bổ sung cờ ngăn chặn việc chuyển trang khi chưa bấm Search (biến

toàn cục)

''' <summary> ''' Cờ dùng để ngăn các thao tác chuyển trang ''' </summary> ''' <remarks></remarks> Dim isDirty As Boolean

Bước 12: thực hiện bật cờ khi người dùng thay đổi các điều kiện tìm kiếm

Private Sub cboSupp_SelectedIndexChanged(...) ... isDirty = True End Sub Private Sub cboCat_SelectedIndexChanged(...) ... isDirty = True End Sub Private Sub txtProductName_TextChanged(...) ... isDirty = True End Sub Private Sub txtPriceF_ValueChanged(...) ... isDirty = True End Sub Private Sub txtPriceT_ValueChanged(...) ... isDirty = True End Sub

Bước 13: xây dựng hàm thông báo lỗi khi chuyển trang mà chưa bấm Search

Sub RaisePageError() MessageBox.Show("Cần thực hiện Search trước khi chuyển trang.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) End Sub

Page 8: Hdth09 ltudql02-linq-e3

7 LTUDQL2 – TUT05

Bước 14: thực hiện ngăn thao tác chuyển trang từ đầu

Private Sub frmMain_Load(...) Handles MyBase.Load Dim listCat As List(Of Category) = ctx.Categories.ToList() listCat.Insert(0, New Category With {.CategoryID = -1, .CategoryName = "ALL"}) cboCat.DataSource = listCat cboCat.DisplayMember = "CategoryName" cboCat.ValueMember = "CategoryID" Dim listSupp As List(Of Supplier) = ctx.Suppliers.ToList() listSupp.Insert(0, New Supplier With {.SupplierID = -1, .CompanyName = "ALL"}) cboSupp.DataSource = listSupp cboSupp.DisplayMember = "CompanyName" cboSupp.ValueMember = "SupplierID" isDirty = True 'ngăn ko cho thực hiện các thao tác chuyển trang lbPaging.Text = "0/0" SetNavigationState() End Sub

Bước 15: bắt đầu đồng ý cho chuyển trang khi người dùng đã nhấn Search

Private Sub btnSearch_Click(...) Handles btnSearch.Click Dim query As IQueryable(Of Product) = BuildSearchQuery() numberOfPages = Math.Ceiling(query.Count() / PAGE_SIZE) curPage = 1 lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = query.Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) isDirty = False 'bắt đầu cho phép chuyển trang SetNavigationState() End Sub

Bước 16: ngăn chặn các thao tác chuyển trang trái phép trong các hàm điều

hướng trang

Private Sub btnNext_Click(...) Handles btnNext.Click If isDirty Then RaisePageError() Return End If curPage += 1 lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = BuildSearchQuery().Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub

Page 9: Hdth09 ltudql02-linq-e3

8 LTUDQL2 – TUT05

Private Sub btnPrev_Click(...) Handles btnPrev.Click If isDirty Then RaisePageError() Return End If curPage -= 1 lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = BuildSearchQuery().Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub Private Sub btnLast_Click(...) Handles btnLast.Click If isDirty Then RaisePageError() Return End If curPage = numberOfPages lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = BuildSearchQuery().Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub Private Sub btnFirst_Click(...) Handles btnFirst.Click If isDirty Then RaisePageError() Return End If curPage = 1 lbPaging.Text = String.Format("{0}/{1}", curPage, numberOfPages) grid.DataSource = BuildSearchQuery().Skip((curPage - 1) * PAGE_SIZE).Take(PAGE_SIZE) SetNavigationState() End Sub

Page 10: Hdth09 ltudql02-linq-e3

9 LTUDQL2 – TUT05

Bước 17: xây dựng hàm Reset form

Private Sub btnClear_Click(...) Handles btnClear.Click txtProductName.Clear() txtPriceF.Value = 0 txtPriceT.Value = 0 cboCat.SelectedIndex = 0 cboSupp.SelectedIndex = 0 grid.DataSource = Nothing isDirty = True curPage = 0 numberOfPages = 0 lbPaging.Text = "0/0" End Sub

3 Phụ lục (sửa câu truy vấn phép chia trong tutorial trước)

Đề: cho biết danh sách các sinh viên đăng ký tất cả các môn học

Dim ctx As New QLSVDataContext Dim query = ctx.SinhViens.Where( _ Function(sv) sv.DangKyHocPhans.Distinct().Count() = ctx.MonHocs.Count() _ ) grid.DataSource = query