6.1. Giới thiệu lập trình cơ sở dữ liệu (ADO.NET)
Khái niệm: ADO.NET là một bộ công nghệ (các lớp) trong .NET Framework, cung cấp khả năng tương tác (kết nối, truy vấn, cập nhật) với các nguồn dữ liệu, phổ biến nhất là các Hệ quản trị CSDL (như SQL Server, Access, Oracle…).
Mô hình kết nối (Connected Model): Là mô hình làm việc chính của bài này.
Nguyên tắc: Mở kết nối -> Gửi lệnh -> Nhận kết quả -> Đóng kết nối.
Các đối tượng chính: Connection, Command, DataReader.
Chuỗi kết nối (Connection String): Là một chuỗi văn bản chứa “chìa khóa” để kết nối đến CSDL, bao gồm: tên máy chủ (Server), tên CSDL (Database), thông tin xác thực (Username/Password hoặc Tích hợp Windows).
Ví dụ (SQL Server với xác thực Windows):
"Server=MAYTINH\\SQLEXPRESS; Database=QuanLySinhVien; Integrated Security=True;"Ví dụ (SQL Server với xác thực SQL):
"Server=.; Database=QuanLySinhVien; User Id=sa; Password=123;"
6.2. Đối tượng SQLConnection (Kết nối)
(Chúng ta tập trung vào
SqlClientcho SQL Server,OleDbdùng cho các CSDL khác như Access).Không gian tên:
System.Data.SqlClientCông dụng: Đại diện cho một phiên kết nối duy nhất đến CSDL SQL Server.
Phương thức chính:
Open(): Mở kết nối.Close(): Đóng kết nối (Rất quan trọng).
Cú pháp:
C#using System.Data.SqlClient; // ... string chuoiKetNoi = "..."; // Chuỗi kết nối của bạn SqlConnection conn = new SqlConnection(chuoiKetNoi); try { conn.Open(); // ... Làm việc với CSDL ở đây ... MessageBox.Show("Kết nối thành công!"); } catch (Exception ex) { MessageBox.Show("Lỗi kết nối: " + ex.Message); } finally { // Đảm bảo kết nối luôn được đóng if (conn.State == System.Data.ConnectionState.Open) { conn.Close(); } }
6.3. Đối tượng SQLCommand (Lệnh)
Công dụng: Đại diện cho một câu lệnh SQL (SELECT, INSERT, UPDATE, DELETE) hoặc một Stored Procedure cần thực thi tại CSDL.
Thuộc tính chính:
Connection: Chỉ định kết nốiSqlConnectionmà lệnh này sẽ dùng.CommandText: Nội dung câu lệnh SQL (ví dụ:"SELECT * FROM SinhVien").
Các phương thức thực thi (Execute):
ExecuteNonQuery(): Dùng cho các lệnh không trả về dữ liệu (INSERT, UPDATE, DELETE). Nó trả về số dòng bị ảnh hưởng.ExecuteScalar(): Dùng khi câu lệnh chỉ trả về một giá trị duy nhất (ví dụ:SELECT COUNT(*) FROM SinhVien).ExecuteReader(): Dùng cho lệnhSELECTtrả về nhiều dòng, nhiều cột. Nó trả về một đối tượngSqlDataReader.
6.4. Đối tượng SQLDataReader (Đọc dữ liệu)
Công dụng: Cung cấp một cách đọc dữ liệu chỉ tiến (forward-only), hiệu suất cao từ kết quả của lệnh
SELECT.Nguyên tắc: Đọc từng dòng (record) một.
Phương thức chính:
Read(): Di chuyển đến dòng tiếp theo. Trả vềtruenếu còn dòng để đọc,falsenếu đã hết.
Cách lấy dữ liệu: Dùng
ten_reader["TenCot"]hoặcten_reader[chi_so_cot].Ví dụ (SELECT):
C#// (Giả sử conn đã được mở) SqlCommand cmd = new SqlCommand("SELECT MaSV, HoTen FROM SinhVien", conn); SqlDataReader reader = cmd.ExecuteReader(); // Dùng 'while' để đọc từng dòng while (reader.Read()) { // Lấy dữ liệu từ dòng hiện tại string maSV = reader["MaSV"].ToString(); string hoTen = reader["HoTen"].ToString(); // (Hiển thị lên ListBox hoặc ComboBox...) listBox1.Items.Add(maSV + " - " + hoTen); } // Phải đóng Reader sau khi dùng xong reader.Close(); // (Sau đó đóng conn)
6.5. Đối tượng SQLParameter (Tham số)
Vấn đề (SQL Injection): Nếu bạn ghép chuỗi trực tiếp từ TextBox vào câu SQL, hacker có thể nhập code độc hại.
Code nguy hiểm:
string sql = "SELECT * FROM Users WHERE User='" + txtUser.Text + "';"Hacker nhập:
' OR 1=1 --Câu lệnh trở thành:
SELECT * FROM Users WHERE User='' OR 1=1 --';(Luôn đúng, bypass login).
Giải pháp (
SqlParameter): Dùng tham số (dấu@) để truyền giá trị vào CSDL một cách an toàn. ADO.NET sẽ tự xử lý các ký tự đặc biệt.Ví dụ (INSERT an toàn):
C#// (Giả sử conn đã mở, có txtMaSV và txtHoTen) string sql = "INSERT INTO SinhVien (MaSV, HoTen) VALUES (@Ma, @Ten)"; SqlCommand cmd = new SqlCommand(sql, conn); // Thêm tham số cmd.Parameters.AddWithValue("@Ma", txtMaSV.Text); cmd.Parameters.AddWithValue("@Ten", txtHoTen.Text); // Thực thi (dùng ExecuteNonQuery) int soDongAnhHuong = cmd.ExecuteNonQuery(); if (soDongAnhHuong > 0) { MessageBox.Show("Thêm thành công!"); } // (Đóng conn)
6.6. Câu hỏi ôn tập
ADO.NET là gì?
Chuỗi kết nối (Connection String) dùng để làm gì? Nêu 3 thông tin quan trọng trong đó.
Trình bày 3 phương thức Execute của
SqlCommandvà công dụng của chúng?SqlDataReaderdùng để làm gì? Tại sao phải đóng nó sau khi dùng?Tại sao phải dùng
SqlParameterthay vì cộng chuỗi trực tiếp vào câu lệnh SQL?
6.7. Bài tập thực hành :
Yêu cầu chuẩn bị:
Cài đặt SQL Server (Express).
Tạo một CSDL tên
QuanLySinhVien.Tạo một bảng tên
SinhViencó 3 cột:MaSV(varchar(10), Primary Key),HoTen(nvarchar(100)),NgaySinh(date).
Bài tập 6.1: Tạo Form Quản lý Sinh viên
Tạo dự án Windows Forms Application tên
BaiTap6_QLSV.Thiết kế giao diện gồm:
3
Label(MaSV, HoTen, NgaySinh).3
TextBox(têntxtMaSV,txtHoTen,txtNgaySinh).4
Button(tênbtnThem,btnSua,btnXoa,btnTimKiem).1
ListBox(tênlstSinhVien) để hiển thị danh sách.
Hướng dẫn làm bài
Đây là bài mở đầu cho Bài 6: Truy xuất dữ liệu với ADO.NET. Trước khi bắt đầu tạo Form, chúng ta cần một bước chuẩn bị “ngầm” rất quan trọng là tạo Cơ sở dữ liệu (CSDL) để sau này Form có nơi để lưu dữ liệu.
🛠️ Bước 0: Chuẩn bị Cơ sở dữ liệu (SQL Server)
Vì bài này về quản lý sinh viên, bạn cần tạo bảng trong SQL Server trước.
Mở SQL Server Management Studio.
Tạo một CSDL mới tên là
QuanLySinhVien.
Tạo một bảng tên SinhVien bằng câu lệnh SQL sau (hoặc tạo bằng giao diện):
SQL
CREATE TABLE SinhVien (
MaSV VARCHAR(20) PRIMARY KEY,
HoTen NVARCHAR(100),
NgaySinh DATE
);Nhập sẵn 1-2 dòng dữ liệu mẫu để lát nữa hiển thị lên ListBox cho dễ kiểm tra.
🖥️ Bước 1: Tạo dự án Windows Forms
Mở Visual Studio 2008
Chọn File -> New -> Project….
Chọn Visual C# -> Windows Forms Application.
Đặt tên (Name) là:
BaiTap6_QLSV.Nhấn OK.
🎨 Bước 2: Thiết kế giao diện (Kéo – Thả)
Chúng ta sẽ sắp xếp các công cụ (Controls) từ Toolboxvào Form.
Gợi ý bố cục:
Phần nhập liệu (Bên trái): Các Label và TextBox xếp thẳng hàng dọc.
Phần nút bấm (Bên dưới phần nhập liệu): Các Button xếp ngang hàng.
Phần hiển thị (Bên phải hoặc dưới cùng): ListBox to để chứa danh sách.
Thao tác:
3 Label: Kéo 3
Labeltừ Toolbox thả vào Form.3 TextBox: Kéo 3
TextBoxthả vào bên cạnh các Label tương ứng.4 Button: Kéo 4
Buttonthả vào Form.1 ListBox: Kéo 1
ListBoxthả vào Form (kéo dãn nó to ra để chứa được nhiều dòng).
⚙️ Bước 3: Thiết lập thuộc tính (Properties)
Đây là bước quan trọng nhất để khớp với yêu cầu của đề bài. Bạn cần đặt tên (Name) chính xác để viết code ở các bài sau.
Chọn từng đối tượng trên Form và sửa trong cửa sổ Properties theo bảng sau:
A. Nhóm Nhập liệu (Label & TextBox)
| Đối tượng | Thuộc tính (Property) | Giá trị (Value) | Ghi chú |
| Label 1 | Text | Mã Sinh viên: | Nhãn hiển thị |
| TextBox 1 | (Name) | txtMaSV | Tên dùng trong code |
| Label 2 | Text | Họ và Tên: | |
| TextBox 2 | (Name) | txtHoTen | |
| Label 3 | Text | Ngày sinh: | |
| TextBox 3 | (Name) | txtNgaySinh | Lưu ý: Nhập dạng ngày tháng |
B. Nhóm Nút bấm (Button)
| Đối tượng | Thuộc tính (Property) | Giá trị (Value) | Ghi chú |
| Button 1 | (Name) | btnThem | Nút Thêm |
| Text | Thêm | ||
| Button 2 | (Name) | btnSua | Nút Sửa |
| Text | Sửa | ||
| Button 3 | (Name) | btnXoa | Nút Xóa |
| Text | Xóa | ||
| Button 4 | (Name) | btnTimKiem | Nút Tìm kiếm |
| Text | Tìm kiếm |
C. Nhóm Hiển thị (ListBox) & Form
| Đối tượng | Thuộc tính (Property) | Giá trị (Value) |
| ListBox 1 | (Name) | lstSinhVien |
| Form1 | Text | Quản lý Sinh viên |
| StartPosition | CenterScreen (Để Form hiện giữa màn hình) |
🏁 Bước 4: Chạy thử giao diện
Nhấn F5 để chạy chương trình.
Lúc này, bạn sẽ thấy giao diện đã hoàn thành. Bạn có thể gõ chữ vào các ô, bấm nút (nhưng chưa có gì xảy ra vì chưa viết code).
Bài tập 6.2: Viết code cho các chức năng (CRUD)
Tạo Lớp kết nối (Nâng cao): Tạo một lớp tĩnh (ví dụ:
Database.cs) để quản lý chuỗi kết nối và các hàm Open/Close Connection chung.Code Nút “Thêm” (
btnThem_Click):Viết câu lệnh
INSERTsử dụngSqlParameter(như ví dụ 6.5).Sử dụng
ExecuteNonQuery().Sau khi thêm, gọi lại hàm tải dữ liệu lên
ListBox.
Code Nút “Xóa” (
btnXoa_Click):Lấy
MaSVtừtxtMaSV.Viết câu lệnh
DELETE FROM SinhVien WHERE MaSV = @Ma.Dùng
SqlParameterđể thêm@Ma.Sử dụng
ExecuteNonQuery().
Code Nút “Sửa” (
btnSua_Click):Viết câu lệnh
UPDATE SinhVien SET HoTen = @Ten, NgaySinh = @Ngay WHERE MaSV = @Ma.Dùng
SqlParametercho cả 3 tham số.Sử dụng
ExecuteNonQuery().
Tải dữ liệu lên ListBox (Hàm
LoadData):Viết một hàm riêng:
void LoadData()Bên trong hàm:
Mở kết nối.
Dùng
SqlCommandvới câu lệnh"SELECT * FROM SinhVien".Dùng
SqlDataReader(như ví dụ 6.4).Trong vòng lặp
while (reader.Read()), thêm dữ liệu vàolstSinhVien(ví dụ:lstSinhVien.Items.Add(reader["MaSV"] + " | " + reader["HoTen"]);).Nhớ
lstSinhVien.Items.Clear()trước vòng lặp.Đóng
ReadervàConnection.
Gọi hàm
LoadData()này trong sự kiệnForm_Loadvà sau mỗi lần Thêm, Sửa, Xóa.
(Nâng cao) Click vào ListBox: Viết sự kiện
lstSinhVien_SelectedIndexChangedđể khi người dùng chọn 1 dòng, thông tin sinh viên đó được hiển thị ngược lại lên cácTextBox.
Hướng dẫn làm bài:
🔗 Bước 1: Khai báo thư viện và Chuỗi kết nối
Để làm việc với SQL Server, việc đầu tiên là phải “gọi” thư viện System.Data.SqlClient.
Nhấn F7 (hoặc chuột phải vào Form -> View Code) để mở màn hình viết code (
Form1.cs).Kéo lên trên cùng, thêm dòng này vào danh sách
using:C#using System.Data.SqlClient; // Thư viện để làm việc với SQL ServerBên trong
class Form1, khai báo biến kết nối toàn cục:C#public partial class Form1 : Form { // 1. Tạo chuỗi kết nối // Lưu ý: Thay "TÊN_MÁY_CỦA_BẠN\\SQLEXPRESS" bằng tên server thực tế của bạn // Nếu dùng SQL xác thực bằng quyền Windows: Integrated Security=True string strKetNoi = @"Data Source=.\SQLEXPRESS;Initial Catalog=QuanLySinhVien;Integrated Security=True"; // 2. Khai báo đối tượng kết nối SqlConnection conn = null; public Form1() { InitializeComponent(); } // ...
🔄 Bước 2: Viết hàm LoadData() (Tải dữ liệu lên ListBox)
Chúng ta cần một hàm dùng chung để tải lại danh sách sinh viên lên ListBox mỗi khi mở Form hoặc sau khi Thêm/Sửa/Xóa xong. Hàm này sử dụng SqlDataReader.
Viết hàm này bên dưới hàm Form1 (Constructor):
private void LoadData()
{
try
{
// Khởi tạo kết nối
conn = new SqlConnection(strKetNoi);
conn.Open(); // Mở kết nối
// Tạo câu lệnh truy vấn
string sql = "SELECT * FROM SinhVien";
SqlCommand cmd = new SqlCommand(sql, conn);
// Thực thi và đọc dữ liệu (Dùng SqlDataReader)
SqlDataReader reader = cmd.ExecuteReader();
// Xóa danh sách cũ trong ListBox trước khi nạp mới
lstSinhVien.Items.Clear();
// Đọc từng dòng
while (reader.Read())
{
string ma = reader["MaSV"].ToString();
string ten = reader["HoTen"].ToString();
// Hiển thị dạng: "SV01 - Nguyễn Văn A"
lstSinhVien.Items.Add(ma + " - " + ten);
}
reader.Close(); // Đóng đầu đọc
}
catch (Exception ex)
{
MessageBox.Show("Lỗi tải dữ liệu: " + ex.Message);
}
finally
{
// Luôn đóng kết nối
if (conn != null) conn.Close();
}
}
Thêm sự kiện Form_Load:
Quay lại giao diện thiết kế (
Design).Nhấp đúp vào nền của Form (chỗ trống bất kỳ).
Trong hàm
Form1_Load, gọi hàmLoadData():C#private void Form1_Load(object sender, EventArgs e) { LoadData(); // Tải dữ liệu ngay khi mở phần mềm }
➕ Bước 3: Viết code nút THÊM (btnThem)
Nút này sử dụng câu lệnh INSERT. Chúng ta phải dùng SqlParameter để tránh lỗi nếu tên có dấu nháy đơn và bảo mật (chống SQL Injection).
Nhấp đúp vào nút Thêm.
Viết code:
private void btnThem_Click(object sender, EventArgs e)
{
try
{
conn = new SqlConnection(strKetNoi);
conn.Open();
// Câu lệnh INSERT với tham số (@Ma, @Ten, @Ngay)
string sql = "INSERT INTO SinhVien (MaSV, HoTen, NgaySinh) VALUES (@Ma, @Ten, @Ngay)";
SqlCommand cmd = new SqlCommand(sql, conn);
// Gán giá trị từ TextBox vào tham số
cmd.Parameters.AddWithValue("@Ma", txtMaSV.Text);
cmd.Parameters.AddWithValue("@Ten", txtHoTen.Text);
// Lưu ý: txtNgaySinh phải nhập đúng định dạng (VD: 2000-01-01)
cmd.Parameters.AddWithValue("@Ngay", txtNgaySinh.Text);
// Thực thi lệnh (ExecuteNonQuery dùng cho Insert/Update/Delete)
int ketQua = cmd.ExecuteNonQuery();
if (ketQua > 0)
{
MessageBox.Show("Thêm thành công!");
LoadData(); // Tải lại danh sách để thấy sinh viên mới
}
}
catch (Exception ex)
{
MessageBox.Show("Lỗi thêm: " + ex.Message);
}
finally
{
if (conn != null) conn.Close();
}
}
❌ Bước 4: Viết code nút XÓA (btnXoa)
Nút này dùng lệnh DELETE. Chúng ta xóa dựa trên MaSV (Khóa chính).
Nhấp đúp vào nút Xóa.
Viết code:
private void btnXoa_Click(object sender, EventArgs e)
{
// Hỏi người dùng có chắc chắn xóa không
if (MessageBox.Show("Bạn có chắc muốn xóa?", "Cảnh báo", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
try
{
conn = new SqlConnection(strKetNoi);
conn.Open();
string sql = "DELETE FROM SinhVien WHERE MaSV = @Ma";
SqlCommand cmd = new SqlCommand(sql, conn);
// Chỉ cần truyền Mã SV để xóa
cmd.Parameters.AddWithValue("@Ma", txtMaSV.Text);
int ketQua = cmd.ExecuteNonQuery();
if (ketQua > 0)
{
MessageBox.Show("Xóa thành công!");
LoadData();
// Xóa trắng các ô nhập liệu
txtMaSV.Text = ""; txtHoTen.Text = ""; txtNgaySinh.Text = "";
}
}
catch (Exception ex)
{
MessageBox.Show("Lỗi xóa: " + ex.Message);
}
finally
{
if (conn != null) conn.Close();
}
}
}
✏️ Bước 5: Viết code nút SỬA (btnSua)
Nút này dùng lệnh UPDATE. Nó cập nhật Tên và Ngày sinh dựa trên Mã SV.
Nhấp đúp vào nút Sửa.
Viết code:
private void btnSua_Click(object sender, EventArgs e)
{
try
{
conn = new SqlConnection(strKetNoi);
conn.Open();
string sql = "UPDATE SinhVien SET HoTen = @Ten, NgaySinh = @Ngay WHERE MaSV = @Ma";
SqlCommand cmd = new SqlCommand(sql, conn);
// Truyền đủ 3 tham số
cmd.Parameters.AddWithValue("@Ma", txtMaSV.Text);
cmd.Parameters.AddWithValue("@Ten", txtHoTen.Text);
cmd.Parameters.AddWithValue("@Ngay", txtNgaySinh.Text);
int ketQua = cmd.ExecuteNonQuery();
if (ketQua > 0)
{
MessageBox.Show("Sửa thành công!");
LoadData();
}
}
catch (Exception ex)
{
MessageBox.Show("Lỗi sửa: " + ex.Message);
}
finally
{
if (conn != null) conn.Close();
}
}
🧪 Bước 6: Chạy và Kiểm thử
Nhấn F5 để chạy chương trình.
Thử Thêm: Nhập Mã “SV001”, Tên “Nguyễn Văn A”, Ngày “2000-01-01”. Nhấn Thêm. Xem ListBox có hiện lên không.
Thử Sửa: Nhập lại Mã “SV001”, đổi tên thành “Nguyễn Văn B”. Nhấn Sửa.
Thử Xóa: Nhập Mã “SV001”. Nhấn Xóa.
