C# Programming

Làm việc với cơ sở dữ liệu – ADO.NET
13 Votes

Trong bài viết hôm nay, chúng ta sẽ tìm hiểu về cách thức truy cập đến dữ liệu thông qua mô hình ADO.NET. Từ đó, các bạn có thể tự mình viết một ứng dụng đơn giản có sử dụng cơ sở dữ liệu

ADO.NET là mô hình mà các ứng dụng .NET sử dụng để có thể làm việc với các hệ quản trị cơ sở dữ liệu cũng như các nguồn dữ liệu khác (gọi chung là nguồn dữ liệu – data sources). Trước khi .NET xuất hiện thì mô hình được sử dụng để kết nối là ADO (ActiveX Data Object). Với sự ra đời của .NET thì ADO.NET cũng được phát triển để có thể giúp lập trình viên viết ứng dụng làm việc với các nguồn dữ liệu dễ dàng hơn và thống nhất hơn nhờ việc sử dụng chung các kiểu dữ liệu, các mẫu thiết kế (design pattern) và các quy tắc đặt tên.

ADO.NET được thiết kế với các tính năng: kiến trúc dữ liệu không kết nối (disconnected data architecture), tích hợp với XML, và được tối ưu để kết nối với cơ sở dữ liệu (và các nguồn dữ liệu khác). Mặc khác, mô hình lập trình ADO.NET cũng khá giống ADO, do đó rút ngắn thời gian tìm hiểu cho những ai đã sử dụng qua ADO trong quá khứ.

Tham khảo thêm: http://msdn.microsoft.com/en-us/library/e80y5yhx(v=vs.80).aspx

Đó là các thông tin tổng quan chung về ADO.NET, sau đây chúng ta sẽ tìm hiểu chi tiết về ADO.NET qua một ví dụ mẫu để hiểu được cách thức làm việc của ADO.NET là như thế nào, các thành phần bên trong nó gồm có những gì và sử dụng chúng ra sao. Chúng ta sẽ viết một chương trình đơn giản dùng để quản lý danh sách, giao diện của chương trình như sau:

image

Sau khi hoàn thành ứng dụng này, chúng ta sẽ có được các kiến thức sau đây:

Ôn lại kiến thức về Windows Forms
Biết được cách tạo kết nối và truy xuất dữ liệu từ SQL Server
Biết được cách thực thi câu lệnh SQL từ ứng dụng
Hiểu cơ bản về Data Binding
Biết cách sử dụng DataGridView
OK, let’s get started

Trước hết, chúng ta tạo một ứng dụng Windows Forms Application bằng ngôn ngữ C# trong Visual Studio và đặt tên cho ứng dụng này là ADONET (hoặc là tên gì đó mà bạn thích)

Đổi thuộc tính Name của Form1 thành MainForm và thay đổi một số thuộc tính như sau:

StartPosition: CenterScreen, mục đích là để form xuất hiện giữa màn hình lúc chạy
Text: “Quản lý danh sách”, tiêu đề của form
Tiếp theo, chúng ta kéo control DataGridView và các Label, TextBox từ ToolBox vào trong Form cùng để có được giao diện như sau, tên của các control được đặt lại là:

DataGridView: gridStudent
Nút Thêm: btnAdd; nút Xóa: btnDelete; nút Lưu: btnSave
Tên: txtName; Tuổi: txtAge; Email: txtEmail và Địa chỉ: txtAddress
image

Thông tin lưu trữ của chúng ta là sinh viên và mỗi người bao gồm các thông tin sau đây: Họ tên, Tuổi, Email, Địa chỉ. Để tạo cơ sở dữ liệu, chúng ta chọn trong cửa sổ Server Explorer chức năng Connect To Database (nếu không thấy cửa số Server Explorer thì chọn từ Menu View-> Server Explorer). Lưu ý, máy tính phải được cài đặt SQL Server trước. Mặc định khi cài Visual Studio, các bạn đã được cài SQL Server Express

image

Hộp thoại Connect To Database xuất hiện, chúng ta sẽ thấy hộp thoại có giao diện như sau:

image

Một số thành phần trong hộp thoại:

Data Source: chỉ định nguồn cung cấp dữ liệu cho ứng dụng. Mặc định là SQL Server, nếu thao tác với các nguồn dữ liệu khác thì chúng ta có thể thay đổi bằng cách nhất nút Change và chọn trong danh sách các loại máy chủ cung cấp dữ liệu
Server Name: Thông thường, nếu bạn cài đặt SQL Server Express thì tên máy chủ sẽ là “.\SQLEXPRESS” (trong đó dấu .\ có nghĩa là máy chủ chính là máy tính đang dùng – local), còn nếu như là phiên bản SQL Server đầy đủ thì tên máy chủ thường là “.\”. Để biết cụ thể tên máy chủ của mình là gì, các bạn mở SQL Server Configuration Manager (All Program-> Microsoft SQL Server –> Configuration Tools –> SQL Server Configuration Manager). Các bạn sẽ thấy danh sách các máy chủ SQL hiện có trong máy kèm theo tên. Ví dụ trong hình sau thì máy tính hiện tại có sử dụng SQL Server Express (tên máy chủ ở trong dấu ngoặc đơn).image
Log on to the server: chọn kiểu chứng thực để đăng nhập vào Cơ sở dữ liệu. Nếu bạn nào đã từng học qua môn CSDL thì sẽ biết tác dụng của phần này, mục đích của nó là xác nhận tài khoản được phép truy cập vào SQL Server. Mặc định của SQL Express là Windows Authentication, còn đối với các phiên bản khác thì tùy chọn này phụ thuộc lúc bạn cài đặt SQL Server.
Connect to a database: nếu như các bạn đã gõ Server Name ở trên rồi thì các bạn sẽ có thể nhập vào tên database cần tạo ra trong ô “Select or enter a database name”. Nếu như muốn sử dụng một database đã có sẵn thì chúng ta nhấp vào mũi tên xổ xuống để chọn database. Phần Attach a database file cho phép các bạn tạo ra một tập tin database nằm chung với ứng dụng. Khi đó, nếu như sao chép ứng dụng qua nơi khác thì tập tin này cũng được copy theo cùng. Sau khi thực hiện xong, các bạn nhấn Test Connection để kiểm tra. Nếu như hộp thoại MessageBox hiện thông báo Test Connection Succeeded thì các bạn có thể tiếp tục thực hiện phần tiếp theo. Còn không thì các bạn cần phải kiểm tra lại các thông tin mình cung cấp có chính xác hay chưa (Server Name, Authentication, Database Name…)
Sau khi tạo thành công database, chúng ta sẽ tạo bảng dữ liệu. Trong cửa sổ Server Explorer, các bạn chọn vào database vừa tạo, mở đến nhãn Table, nhấn chuột phải chọn Add New Table.

image

Table này sẽ có các cột như sau:

image

Trường ID của chúng ta có kiểu nguyên và là trường tự động tăng (chắc các bạn vẫn còn nhớ tạo khóa IDENTITY trong môn CSDL). Để cho trường này là tự động tăng, các bạn chọn dòng của trường này và thay đổi giá trị Identity trong cửa sổ Column Properties ngay bên dưới. Chi tiết các bạn xem hình vẽ sau:

image

Lưu lại bảng này với tên Student. Sau đó, các bạn nhập vào một vài dòng dữ liệu để test ứng dụng. Nhấp chuột phải lên tên bảng, chọn Show Table Data và thực hiện việc nhập dữ liệu. Lưu ý là chúng ta không cần phải nhập dữ liệu vào cột ID bởi vì dữ liệu cột này sẽ được tự động sinh ra

image

Như vậy là chúng ta xong phần thiết lập cơ sở dữ liệu. Công việc cần thiết bây giờ là kết nối đến cơ sở dữ liệu, lấy dữ liệu ra sử dụng. Chúng ta hoàn toàn có thể sử dụng các Wizard để thực hiện các kết nối đến CSDL, tuy nhiên, trong ứng dụng này chúng ta sẽ viết code để hiểu rõ chi tiết hoạt động của các thành phần là như thế nào.

Đoạn code sau đây được khai báo trong lớp của Form chúng ta vừa tạo (chọn Form, nhấp chuột phải chọn View Code hoặc nhấn F7)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Tạo kết nối đến database
SqlConnection connection = new SqlConnection(@"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\Users\Xuan Chien\Desktop\ITIC\ADONET\StudentDB.mdf;Integrated Security=True;User Instance=True");

//Data Adapter
SqlDataAdapter da;

//DataSet dùng để lưu trữ lại dữ liệu lấy từ SQL Server
DataSet ds = new DataSet();

//ID hiện tại
int studentID = -1;

//Nếu người dùng dùng chức năng thêm thì add=true
bool add = false;
Giải thích:

SqlConnection: dùng để chỉ rõ là chúng ta kết nối đến đâu. Để chỉ định các thông tin như Server Name, Database Name, Authentication thì ta cần phải truyền vào cho constructor của lớp SqlConnection một chuỗi kết nối (Connection String). Chi tiết về chuỗi kết nối thì các bạn xem tại: connectionstrings.com. Nói một cách tổng quát, chuỗi kết nối là cách chúng ta truyền vào thông tin cách thức kết nối đến nguồn dữ liệu. Ví dụ, xét chuỗi kết nối sau: “Data Source=.\sqlexpress;Initial Catalog=AdventureWorksLT2008;Integrated Security=True”. Trong chuỗi kết nối, chúng ta đã chỉ định rằng Server của SQL Server là .\sqlexpress, CSDL được dùng là AdventureWorksLT2008, cơ chế chứng thực là Windows Authentication.
DataSet: đây là đối tượng mà sẽ chứa toàn bộ dữ liệu chúng ta truy xuất được từ database. Ứng dụng của chúng ta sẽ thao tác trên dữ liệu trong DataSet này. Sử dụng DataSet cho phép chúng ta chỉ truy xuất dữ liệu từ database 1 lần duy nhất và kết nối đến Database sẽ được ngắt đi ngay sau khi truy xuất.
SqlDataAdapter: đối tượng của lớp này hoạt động như là một cầu nối để đưa dữ liệu từ database đổ vào DataSet. Đối tượng lớp này chịu trách nhiệm tự động quản lý đóng mở kết nối đến database.
studentID và add: giải thích sau
Đoạn code quan trọng tiếp theo nằm trong hàm xử lý sự kiện Load của Form dùng để đưa dữ liệu lên Grid. Nội dung đoạn code như sau:

1
2
3
4
5
6
7
8
9
10
11
//Tạo Data Adapter
da = new SqlDataAdapter("Select * from Student", connection);

//Data Adapter sẽ tự động sinh ra khóa chính từ khóa chính ở Database
da.MissingSchemaAction = MissingSchemaAction.AddWithKey;

//Kết nối và đưa dữ liệu vào DataSet
da.Fill(ds, "Student");

//Hiển thị lên Data Grid View (Data Binding)
gridStudent.DataSource = ds.Tables["Student"];
Chúng ta trước khởi tạo một đối tượng SqlDataAdapter, cung cấp cho constructor câu lệnh Select để lấy dữ liệu và đối tượng chứa kết nối đến Database. Sau đó, chúng ta chỉnh giá trị của thuộc tính MissingSchemaAction sang AddWithKey để yêu cầu DataAdapter ánh xạ trường khóa chính vào trong DataSet. Nhờ việc thiết lập này mà sau này ta có thể tìm kiếm một dòng dữ liệu trên DataSet bằng cách cung cấp giá trị khóa. Đến đây thì việc kết nối đến CSDL và truy xuất vẫn chưa được thực hiện. Chỉ khi các bạn gọi phương thức Fill của DataAdapter thì lúc này dữ liệu mới được lấy từ Database và đổ vào DataSet. Tham số thứ 2 của phương thức Fill là chỉ định tên của bảng dữ liệu mà chúng ta sẽ quy định cho dữ liệu được đưa vào DataSet. Tên này không nhất thiết là phải giống như tên bảng dưới cơ sở dữ liệu mà có thể là một cái tên bất kỳ.

Dữ liệu đã được lấy ra, việc cuối cùng chỉ là hiển thị nó lên giao diện người dùng. Chúng ta sẽ chỉ định thuộc tính DataSource của gridStudent là ds.Tables[“Student”]. Điều này có nghĩa là sử dụng dữ liệu nằm trong một bảng có tên “Student” ở trong DataSet. Các bạn cần nhớ rằng 1 DataSet thì có thể chứa nhiều dữ liệu từ các bảng khác nhau. Việc gắn dữ liệu từ Database lên giao diện được gọi là DataBinding. Đối với các control khác như ListBox hay ComboBox thì chúng ta cũng có thể Binding dữ liệu vào thông qua thuộc tính DataSource và các thuộc tính DisplayMember, DisplayValue

Sau khi đã hoàn thành các bước ở trên thì khi chạy chương trình, chúng ta sẽ có được giao diện như sau:

image

Việc tiếp theo đó là chúng ta phải hiển thị thông tin chi tiết của dòng dữ liệu đang được chọn trong Grid vào các TextBox bên phải cửa sổ. DataGridView cung cấp cho chúng ta sự kiện RowEnter. Sự kiện này sẽ được gọi mỗi khi có một ô trên dòng nào đó được chọn (RowIndex thay đổi). Phương thức xử lý của sự kiện này là như sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void gridStudent_RowEnter(object sender, DataGridViewCellEventArgs e)
{
//Người dùng đang trong chế độ chỉnh sửa dữ liệu
add = false;

//Lấy ID của dòng hiện tại đang được chọn trong GridView
studentID = Int32.Parse(gridStudent.Rows[e.RowIndex].Cells["ID"].Value.ToString());

//Lấy ra dòng dữ liệu được chọn trong DataSet
DataRow dr = ds.Tables["Student"].Rows.Find(studentID);

//Hiển thị thông tin lên các TextBox
txtName.Text = dr["Name"].ToString();
txtAge.Text = dr["Age"].ToString();
txtEmail.Text = dr["Email"].ToString();
txtAddress.Text = dr["Address"].ToString();
}
Trước hết, chúng ta thiết lập giá trị cho biến add thành false. Mục đích của biến này là để cho ứng dụng của chúng ta biết là chúng ta đang thêm mới một dòng dữ liệu hay là đang chỉnh sửa dữ liệu hiện có khi nhấn nút “Lưu”. Vì khi người dùng chọn vào một dòng, thông tin dữ liệu của dòng này được hiển thị và cho phép người dùng chỉnh sửa nên biến add được thiết lập giá trị là false.

Tiếp theo đó, chúng ta lấy ra ID của dòng dữ liệu được chọn. Các bạn để ý thấy rằng khi chạy chương trình, chúng ta có một cột dữ liệu có tên là ID. Dựa vào cột ID này mà chúng ta biết được ID của dòng dữ liệu được chọn. Giá trị ID này được lưu vào biến studentID của form nhằm sử dụng cho trường hợp cập nhật dữ liệu trong sự kiện của nút “Lưu”

Đã có được ID của dòng dữ liệu, bây giờ chúng ta chỉ cần lấy ra dòng dữ liệu thực sự chứa ID này trong DataSet của chúng ta. Việc truy xuất này khá đơn giản, chỉ cần chúng ta chỉ định bảng nào trong DataSet cần lấy dữ liệu và dùng phương thức Find của tập Rows trên bảng dữ liệu đó. Các bạn có thể thắc mắc là làm sao nó biết được phải tìm kiếm trên cột nào trong bảng. Theo mặc định nó sẽ tìm kiếm trên khóa chính của bảng, đó là lý do tại sao chúng ta phải thiết lập thuộc tính MissionSchemaAction của DataAdapter. Sau khi lấy được dòng dữ liệu này rồi thì chúng ta thiết lập giá trị Text của các TextBox nhằm hiển thị dữ liệu.

Nhấn F5 để chạy chương trình, kết quả chúng ta có được là như sau:

image

Công việc cuối cùng lúc bấy giờ chính là viết sự kiện xử lý cho các button của chúng ta. Hãy lần lượt đi qua từng phương thức xử lý sự kiện click của mỗi Button để hiểu rõ chức năng và cách chúng hoạt động.

Thêm
Khi người dùng nhấn nút “Thêm”, chúng ta chỉ đơn giản là xóa nội dung Text trong những TextBox để người dùng nhập thông tin mới, đồng thời thay đổi biến add thành true (các bạn có thể dễ dàng nhận thấy add hoạt động như một biến cờ để thông báo về sự thay đổi trạng thái)

1
2
3
4
5
6
7
8
//Xóa nội dung trong các TextBox chuẩn bị cho dữ liệu nhập
txtName.Text = "";
txtAge.Text = "";
txtAddress.Text = "";
txtEmail.Text = "";

//Chuyển sang chế độ Thêm dữ liệu
add = true;
Xóa
Khi người dùng nhấn nút “Xóa”, chúng ta trước hết kiểm tra xem thực sự có dòng nào đang được chọn trên gridStudent hay không, sau đó hiển thị một MessageBox hỏi người dùng có chắc chắn muốn xóa dữ liệu này. Nếu người dùng đồng ý thì chúng ta thực hiện xóa dòng dữ liệu tương ứng trên DataSet và cập nhật xuống database

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void btnDelete_Click(object sender, EventArgs e)
{
//Chắc chắn rằng người dùng đang chọn một dòng trên grid
if (gridStudent.SelectedCells.Count == 0)
return;

//Hiển thị thông báo xóa dữ liệu
DialogResult result = MessageBox.Show("Bạn có thật sự muốn xóa dòng dữ liệu này?", "Thông báo", MessageBoxButtons.YesNo);
if (result == System.Windows.Forms.DialogResult.Yes)
{
DataRow dr = ds.Tables["Student"].Rows.Find(studentID);
dr.Delete();
SqlCommandBuilder commandBuiler = new SqlCommandBuilder(da);
da.Update(ds, "Student");
}
}
Các bạn lưu ý dòng khởi tạo đối tượng SqlCommandBuilder. Nếu như chúng ta chỉ muốn hiển thị dữ liệu thì dòng này là không cần thiết, tuy nhiên nếu như chúng ta muốn cập nhật dữ liệu (Thêm, Xóa, Sửa) trên Database thì dòng khởi tạo này là cần thiết. Dòng khởi tạo này sẽ thiết lập DataAdapter của chúng ta các câu lệnh Insert, Update và Delete mặc định cho việc cập nhật dữ liệu. Sau khi đã thiết lập xong, chúng ta gọi phương thức Update của DataAdapter. Trong phương thức Update cần phải chỉ rõ là DataSet nào cập nhật và bảng nào trên DataSet được cập nhật xuống CSDL. Nếu như chúng ta không gọi phương thức Update này thì các bạn có thể thấy rằng dữ liệu hiển thị trên Grid vẫn biến mất như là đã bị xóa. Nhưng thực sự lúc đó chúng ta chỉ xóa dữ liệu đó trên DataSet, hoàn toàn tách biệt với Database (các bạn còn nhớ kiến trúc dữ liệu không kết nối của ADO.NET chứ?)
Lưu
Khi người dùng nhấn nút “Lưu” thì cần phải xét 2 trường hợp là liệu người dùng đang thêm một dòng dữ liệu mới hay là người dùng đang cập nhật một dòng dữ liệu hiện có. Tùy vào từng tình huống mà chúng ta sẽ xử lý cho dữ liệu trên DataSet tương ứng

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private void btnSave_Click(object sender, EventArgs e)
{
//Nếu người dùng đang chỉnh sửa dữ liệu
if (!add)
{
//Lấy ra dòng dữ liệu đang được chọn trong DataSet
DataRow dr = ds.Tables["Student"].Rows.Find(studentID);

//Bắt đầu chỉnh sửa dữ liệu
dr.BeginEdit();

//Cập nhật
dr["Name"] = txtName.Text;
dr["Age"] = txtAge.Text;
dr["Email"] = txtEmail.Text;
dr["Address"] = txtAddress.Text;

//Kết thúc chỉnh sửa dữ liệu
dr.EndEdit();
}
else //Người dùng thêm dữ liệu mới
{
//Tạo ra một dòng dữ liệu mới
DataRow dr = ds.Tables["Student"].NewRow();

//Đưa thông tin vào dòng dữ liệu này
dr["Name"] = txtName.Text;
dr["Age"] = txtAge.Text;
dr["Email"] = txtEmail.Text;
dr["Address"] = txtAddress.Text;

//Thêm dòng dữ liệu này vào DataSet
ds.Tables["Student"].Rows.Add(dr);
}

//Tạo đối tượng SqlCommandBuilder để có thể cập nhật dữ liệu
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(da);

//Cập nhật các thay đổi đã thực hiện trên DataSet.
//Không có dòng này thì dữ liệu sẽ không được cập nhật trên database
da.Update(ds, "Student");
}
Nếu như người dùng đang cập nhật một dòng dữ liệu đã có thì chúng ta lấy dòng dữ liệu đó ra và chỉnh sửa lại các thông tin trên dòng dữ liệu đó. Các phương thức ds.BeginEdit() và ds.EndEdit() có tác dụng ngăn chặn các sự kiện khác có thể xảy ra trên dòng dữ liệu này khi gán các giá trị mới cho chúng (ví dụ như kiểm tra Lỗi, Validation, RowStateChanged….). Trường hợp người dùng thêm dữ liệu thì chúng ta sẽ tạo một DataRow mới nhờ phương thức NewRow() của bảng trong DataSet, sau đó chúng ta gán giá trị cho các trường của DataRow và cuối cùng là thêm dòng dữ liệu này vào danh sách các dòng dữ liệu đang có.
Trong cả 2 trường hợp, chúng ta đều cần thiết phải cập nhật dữ liệu xuống database bằng phương thức Update của DataAdapter.

Đến đây thì ứng dụng nhỏ của chúng ta đã hoàn thành. Các bạn nhấn F5 để chạy và thử nghiệm các tính năng Thêm, Xóa, Sửa của ứng dụng.

Download chương trình Demo tại đây: ADONET
Kết luận: ADO.NET là một công nghệ nền tảng của .NET dùng trong việc truy cập vào CSDL. Các đối tượng như DataAdapter và DataSet làm việc kết hợp với nhau tạo nên kiến trúc dữ liệu không kết nối, điều này giúp tăng tốc độ làm việc với CSDL cũng như tiết kiệm tài nguyên hệ thống (không cần phải duy trì kết nối liên tục đến database). Trong các bài tiếp theo, chúng ta sẽ tìm hiểu về một số cách khác có thể giúp bạn truy xuất và cập nhật cơ sở dữ liệu như DataReader hoặc tìm hiểu về công nghệ ADO.NET Entity Model giúp cho các thao tác với CSDL trở nên dễ dàng hơn lúc trước rất nhiều. Chúc các bạn thành công!

About these ads

Nhận xét

Bài đăng phổ biến