Giới thiệu ứng dụng ADONETAPP_DEMO

Phần Demo này sẽ minh hoạ cách tạo chương trình ADONETAPP_DEMO bằng ngôn ngữ Visual Basic (nếu dùng C# có thể dùng các công cụ chuyển đổi mã ). Khi ứng dụng bắt đầu, nó sẽ hiển thị dữ liệu từ các tập tin XML nếu chúng tồn tại. Sau đó, ứng dụng sẽ kiểm tra kết nối đến SQL Server có thành công không, nếu thành công thì các bảng GenreReview sẽ được tạo nếu chúng không tồn tại trong database và sẽ thực hiện đồng bộ dữ liệu (tức cập nhật qua lại) gữa database và ứng dụng. Khi ứng dụng đóng, việc đồng bộ dữ liệu được thực hiện lần nữa và dữ liệu sẽ được lưu đến tập tin XML (serializing).

Cơ sở dữ liệu

Cơ sở dữ liệu ngocminhADO gồm hai bảng ReviewGenre như sau:

Lược đồ của hai bảng:
Genre

Review

Dữ liệu mẫu:

Genre

Review

Làm việc với các lớp phi kết nối (DisConnected Classes)

Demo : làm việc với các lớp phi kết nối

  • Tạo một ứng dụng winform (ngôn ngữ VB hay C#) tên ADONETAPP_DEMO và lưu tại một vị trí nào đó tuỳ ý.
  • Thêm một DataGridView để hiển thị dữ liệu bảng Genre (thuộc tính Name có giá trị là genreData) và một DataGridView để hiển thị dữ liệu từ bảng Review (thuộc tính Name có giá trị là reviewData) vào Form như hình sau:

  • Thêm đoạn mã (phạm vi lớp) tạo một tập tin XML (GenreReview.xsd) chứa lược đồ quan hệ của DataSet và một tập tin XML khác (GenreReview.xml) chứa dữ liệu của DataSet. Đoạn mã được đặt dưới Public Class Form1 như sau:

Imports System.IO

Public Class Form1

'Tạo tập tin lưu trữ lược đồ DataSet

Private ReadOnly xsdFile = Path.Combine("I:\\","GenreReview.xsd")

'Tạo tập tin XML lưu trữ dữ liệu

Private ReadOnly xmlFile = Path.Combine("I:\\","GenreReview.xml")

....

End Class

* Lưu ý rằng hai tập tin được lưu trong ổ đĩa cục bộ I: (chúng ta có thể thay đổi tuỳ ý) và cần thêm namespace System.IO để có thể dùng lớp Path.

  • Khai báo biến (phạm vi lớp) ds để định nghĩa một DataSet:

Imports System.IO

Public Class Form1

'Tạo tập tin lưu trữ lược đồ DataSet

Private ReadOnly xsdFile = Path.Combine("I:\\","GenreReview.xsd")

'Tạo tập tin XML lưu trữ dữ liệu

Private ReadOnly xmlFile = Path.Combine("I:\\","GenreReview.xml")

Private ds As DataSet

....

End Class

  • Tạo phương thức PopulateDataSet() kiểm tra tập tin XML chứa lược đồ (GenreReview.xsd) có tồn tại hay không, nếu có tồn tại thì lược đồ sẽ được nạp vào DataSte, nếu chưa tồn tại thì gọi phương thức CreateSchema() để tạo lược đồ và dữ liệu. Mã cho phương thức PopulateDataSet():

Private Sub PopulateDataSet()

'nếu tồn tại tập tin lưu trữ lược đồ DataSet

If File.Exists(xsdFile) Then

ds = New DataSet()

ds.ReadXmlSchema(xsdFile)

' nếu chưa tồn tại thì tạo lược đồ DataSet

Else

CreateSchema()

End If

' nếu tồn tại tập tin xml thì đọc nó

If File.Exists(xmlFile) Then

ds.ReadXml(xmlFile, XmlReadMode.IgnoreSchema)

End If

End Sub

  • Trong phương thứ PopulateDataSet() có gọi phương thức CreateSchema() để tạo lược đồ (và dữ liệu) cho DataSet và lưu lược đồ đến tập tin XML (GenreReview.xsd). Đoạn mã cho CreateSchema():

Private Sub CreateSchema()

'Tạo một DataSet chứa hai bảng Genre và Review

ds = New DataSet("GenreReview")

' Tạo bảng Genre gồm hai cột là ID (khoá chính) và Name

Dim genre = ds.Tables.Add("Genre")

genre.Columns.Add("ID", GetType(Integer))

genre.Columns.Add("Name", GetType(String))

genre.PrimaryKey = New DataColumn() {genre.Columns("ID")}

Dim genRow As DataRow = Nothing

genRow = genre.NewRow()

genRow("ID") = 100

genRow("Name") = "Pop"

genre.Rows.Add(genRow)

genRow = genre.NewRow()

genRow("ID") = 101

genRow("Name") = "Jazz"

genre.Rows.Add(genRow)

' Tạo bảng Review gồm các cột ID (khoá chính), Title, Summary, GenreId (khoá ngoại)

Dim review = ds.Tables.Add("Review")

Dim pk_re = review.Columns.Add("ID", GetType(Integer))

pk_re.AutoIncrement = True

pk_re.AutoIncrementSeed = -1

pk_re.AutoIncrementStep = -1

review.Columns.Add("Title", GetType(String))

review.Columns.Add("Summary", GetType(String))

review.Columns.Add("GenreId", GetType(Integer))

review.PrimaryKey = New DataColumn() {review.Columns("ID")}

Dim reviewRow As DataRow = Nothing

reviewRow = review.NewRow()

'reviewRow("ID") = 1

reviewRow("Title") = "Title 01"

reviewRow("Summary") = "Summary 01"

reviewRow("GenreId") = 100

review.Rows.Add(reviewRow)

reviewRow = review.NewRow()

'reviewRow("ID") = 2

reviewRow("Title") = "Title 02"

reviewRow("Summary") = "Summary 02"

reviewRow("GenreId") = 101

review.Rows.Add(reviewRow)

reviewRow = review.NewRow()

'reviewRow("ID") = 3

reviewRow("Title") = "Title 03"

reviewRow("Summary") = "Summary 03"

reviewRow("GenreId") = 101

review.Rows.Add(reviewRow)

' thiết lập quan hệ hai bảng Genre và Review

ds.Relations.Add("genre_review", genre.Columns("ID"),

review.Columns("GenreId"))

' lưu dữ liệu DataSet đến tập tin xml

ds.WriteXmlSchema(xsdFile)

End Sub

  • Gọi phương thức PopulateDataSet() và ràng buộc dữ liệu đến các DataGridView trong sự kiện Load của Form:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

PopulateDataSet()

genreData.DataSource = ds

genreData.DataMember = "Genre"

reviewData.DataSource = ds

reviewData.DataMember = "Genre.genre_review"

End Sub

  • Khi đóng Form, chúng ta muốn dữ liệu trong DataSet được lưu đến tập tin xml bằng cách trong sự kiện FormClosing của Form gọi phương thức WriteXML của đối tượng DataSet như sau:

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing

ds.WriteXml(xmlFile, XmlWriteMode.DiffGram)

End Sub

  • Chạy chương trình. Kết quả:

Chọn ID 101 từ bảng Genre

Khi đóng Form, kiểm tra trong ổ đĩa I: sẽ xuất hiện 2 tập tin GenreReview.xsdGenreReview.xml.


Làm việc với các lớp kết nối (Connected Classes)

  • Làm việc với chuỗi kết nối
  • Làm việc với dữ liệu
  • Làm việc với transaction

Demo: làm việc với chuỗi kết nối

  • Tạo và lưu trữ chuỗi kết nối trong tập tin cấu hình ứng dụng (App.config) bằng cách thêm phần tử <connectionString> vào trong phần tử <configuration> dưới phần tử <startup> của tập tin App.config (trong cửa sổ Solution Explorer)

Nội dung trong App.config:


<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<startup>

<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />

</startup>

<connectionStrings>

<add name="db" connectionString= "Data Source=.\SQLEXPRESS; Initial catalog = ngocminhADO;Integrated Security=True;" />

</connectionStrings>

</configuration>

Chuỗi kết nối dùng để kết nối đến SQL Server Express trên máy cục bộ, sử dụng cơ sở dữ liệu ngocminhADO.

  • Thêm một tham chiếu đến thư viện System.Configuration.Dll bằng cách vào menu PROJECT > Add Reference…> Assemblies > Framework > System.Configuration như hình sau:

  • Thêm một biến tên cnString (phạm vi lớp) dùng để tham chiếu đến chuỗi kết nối sau khi nó được đọc từ tập tin cấu hình

Public Class Form1

'Tạo tập tin lưu trữ lược đồ DataSet

Private ReadOnly xsdFile = Path.Combine("I:\\","GenreReview.xsd")

'Tạo tập tin XML lưu trữ dữ liệu

Private ReadOnly xmlFile = Path.Combine("I:\\","GenreReview.xml")

Private ds As DataSet

Private cnString As String

....

End Class

  • Tạo một phương thức tên CheckConnectivity() dùng mở kết nối và sẽ trả về phiên bản SQL Server nếu thành công, ngược lại sẽ phát sinh lỗi. Mã cho phương thức CheckConnectivity() như sau:

Private Function CheckConnectivity() As Boolean

Try

Using cn = New SqlConnection(cnString)

cn.Open()

Dim version = cn.ServerVersion

MessageBox.Show("Kết nối thành công!" & "Phiên bản:" & version)

End Using

Catch ex As Exception

MessageBox.Show(("Lỗi kết nối: " & ex.Message)

Return False

End Try

Return True

End Function

  • Trong sự kiện Load của Form, thêm đoạn mã để nhận thông tin chuỗi kết nối từ tập tin cấu hình (mà biến cnString tham chiếu tới) và thực hiện gọi phương thức CheckConnectivity() để mở kết nối. Đoạn mã trong Form1_Load lúc này như sau:

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

cnString =ConfigurationManager.ConnectionStrings("db").ConnectionString()

CheckConnectivity()

PopulateDataSet()

genreData.DataSource = ds

genreData.DataMember = "Genre"

reviewData.DataSource = ds

reviewData.DataMember = "Genre.genre_review"

End Sub

  • Chạy ứng dụng. Nếu kết nối thành công sẽ hiện thông báo, ví dụ:

Demo: làm việc với dữ liệu

  • Khai báo hai biến daGenredaReview (phạm vi lớp) sẽ tham chiếu đến các Data Adater dùng để di chuyển dữ liệu giữa ứng dụng và database (ngocminhADO). Đoạn mã trông như sau:
Public Class Form1

'Tạo tập tin lưu trữ lược đồ DataSet

Private ReadOnly xsdFile = Path.Combine("I:\\","GenreReview.xsd")

'Tạo tập tin XML lưu trữ dữ liệu

Private ReadOnly xmlFile = Path.Combine("I:\\","GenreReview.xml")

Private ds As DataSet

Private cnString As String

Private daGenre As SqlDataAdapter

Private daReview As SqlDataAdapter

....

End Class

  • Thêm phương thức InitializeDataAdapters() để khởi tạo các Data Adapter cho các bảng GenreReview. Với bảng Genre, data adapter được khởi tạo với SqlDataAdapter và dùng đối tượng SqlCommandBuilder để tạo các lệnh Insert, Update, và Delete. Với bảng Review, data adapter được khởi tạo bằng cách tạo từng đối tượng command và gán các thuộc tính tương ứng. Mã cho phương thức InitializeDataAdapters() như sau:
Public Sub InitializeDataAdapters()

'Genre

daGenre = New SqlDataAdapter("SELECT * FROM Genre", cnString)

Dim bldVehicles As New SqlCommandBuilder(daGenre)

'Review

Dim cn As New SqlConnection(cnString)

Dim cmdSelectReview = cn.CreateCommand()

Dim cmdUpdateReview = cn.CreateCommand()

Dim cmdDeleteReview = cn.CreateCommand()

Dim cmdInsertReview = cn.CreateCommand()

cmdSelectReview.CommandText = "SELECT * FROM Review"

cmdInsertReview.CommandText = "INSERT Review(Title,Summary, GenreId) " & " OUTPUT inserted.* " & " VALUES( @Title, @Summary, @GenreId); "

cmdInsertReview.Parameters.Add("@Title", SqlDbType.NChar, 10, "Title")

cmdInsertReview.Parameters.Add("@Summary", SqlDbType.NVarChar, 50, "Summary")

cmdInsertReview.Parameters.Add("@GenreId", SqlDbType.Int, 0, "GenreId")

cmdUpdateReview.CommandText = "UPDATE Review SET " _

& " Title=@Title, Summary=@Summary, GenreId=@GenreId " _

& " WHERE ID=@OriginalID " _

& " AND Title=@OriginalTitle " _

& " AND Summary=@OriginalSummary" _

& " AND GenreId=@OriginalGenreId"

cmdUpdateReview.Parameters.Add("@OriginalID", SqlDbType.Int, 0, "ID").SourceVersion = DataRowVersion.Original

cmdUpdateReview.Parameters.Add("@Title", SqlDbType.NChar, 10, "Title")

cmdUpdateReview.Parameters.Add("@OriginalTitle", SqlDbType.NChar, 10, "Title").SourceVersion = DataRowVersion.Original

cmdUpdateReview.Parameters.Add("@Summary", SqlDbType.NVarChar, 50, "Summary")

cmdUpdateReview.Parameters.Add("@OriginalSummary", SqlDbType.NVarChar, 50, "Summary").SourceVersion = DataRowVersion.Original

cmdUpdateReview.Parameters.Add("@GenreId", SqlDbType.Int, 0, "GenreId")

cmdUpdateReview.Parameters.Add("@OriginalGenreId", SqlDbType.Int, 0, "GenreId").SourceVersion = DataRowVersion.Original

cmdDeleteReview.CommandText = "DELETE Review " _

& " WHERE ID=@OriginalID " _

& " AND Title=@OriginalTitle " _

& " AND Summary=@OriginalSummary" _

& " AND GenreId=@OriginalGenreId"

cmdDeleteReview.Parameters.Add("@OriginalID", SqlDbType.Int, 0, "ID").SourceVersion = DataRowVersion.Original

cmdDeleteReview.Parameters.Add("@OriginalGenreId", SqlDbType.Int, 0, "GenreId").SourceVersion = DataRowVersion.Original

cmdDeleteReview.Parameters.Add("@OriginalTitle", SqlDbType.NChar, 10, "Title").SourceVersion = DataRowVersion.Original

cmdDeleteReview.Parameters.Add("@OriginalSummary", SqlDbType.NVarChar, 50, "Summary").SourceVersion = DataRowVersion.Original

daReview = New SqlDataAdapter(cmdDeleteReview)

daReview.InsertCommand = cmdInsertReview

daReview.UpdateCommand = cmdUpdateReview

daReview.DeleteCommand = cmdDeleteReview

End Sub
  • Trong thủ tục của sự kiện Load của Form, thay phương thức CheckConnectivity() bằng phương thức InitializeDataAdapters().
  • Tạo một phương thức tên là Synchronize() sẽ được gọi khi ứng dụng bắt đầu và kết thúc. Phương thức sẽ kiểm tra một kết nối đến SQL Server có thành công không; nếu kết nối thành công thì sẽ kiểm tra các bảng (GenreReview) có tồn tại không, nếu không sẽ tạo các bảng này bằng cách gọi phương thức CreateTablesIfNotExisting(). Phương thức Synchronize() cũng gọi một phương thức khác là SyncData() để thực hiện đồng bộ dữ liệu giữa ứng dụng và cơ sở dữ liệu. Mã cho Synchronize() như sau:
Private Sub Synchronize()

If CheckConnectivity() Then

CreateTablesIfNotExisting()

SyncData()

End If

End Sub

  • Trong phương thức CheckConnectivity() bằng cách xoá đi các lệnh Message.Show(…). Phương thức lúc này đơn thuần chỉ trả về giá trị true hay false.
  • Tạo phương thức CreateTablesIfNotExisting() để kiểm tra các bảng (GenreReview) có tồn tại trong cơ sở dữ liệu không (ngocminhADO), nếu không sẽ tạo các bảng này. Đoạn mã cho CreateTablesIfNotExisting():
Private Sub CreateTablesIfNotExisting()

Try

Using cn = New SqlConnection(cnString)

Using cmd = cn.CreateCommand()

cn.Open()

cmd.CommandText = "IF NOT EXISTS ( " _

& " SELECT * FROM sys.Tables_ WHERE NAME='Genre') " _

& " CREATE TABLE Genre( " _

& " ID int PRIMARY KEY, " _

& " Name nchar(10)) "

cmd.ExecuteNonQuery()

cmd.CommandText = "IF NOT EXISTS ( " _

& " SELECT * FROM sys.Tables_ WHERE NAME='Review') " _

& " CREATE TABLE Review( " _

& " ID int IDENTITY PRIMARY KEY, " _

& " Title nchar(10), " _

& " Summary nvarchar(50), " _

& " GenreId int)"

cmd.ExecuteNonQuery()

End Using

End Using

Catch ex As Exception

MessageBox.Show(ex.Message)

End Try

End Sub

  • Tạo phương thức SyncData() để đồng bộ dữ liệu giữa ứng dụng và database. Đoạn mã cho SyncData():
Private Sub SyncData()

'chấp nhận và gửi các thay đổi từ ứng dụng đến database

Try

daGenre.Update(ds, "Genre")

daReview.Update(ds, "Review")

ds.AcceptChanges()

Catch ex As Exception

MessageBox.Show(ex.Message)

End Try

'nhận các thay đổi từ database

Dim tempGenre As New DataTable()

daGenre.Fill(tempGenre)

ds.Tables("Genre").Merge(tempGenre)

Dim tempReview As New DataTable()

daReview.Fill(tempReview)

ds.Tables("Review").Merge(tempReview)

End Sub

  • Gọi phương thức Synchronize() trong phương thức PopulateDataSet():
Private Sub PopulateDataSet()

'nếu tồn tại tập tin lưu trữ lược đồ DataSet

If File.Exists(xsdFile) Then

ds = New DataSet()

ds.ReadXmlSchema(xsdFile)

' nếu chưa tồn tại thì tạo lược đồ DataSet

Else

CreateSchema()

End If

' nếu tồn tại tập tin xml thì đọc nó

If File.Exists(xmlFile) Then

ds.ReadXml(xmlFile, XmlReadMode.IgnoreSchema)

End If

Synchronize()

End Sub

  • Gọi phương thức Synchronize() trong phương thức Form1_FormClosing:
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing

Synchronize()

ds.WriteXml(xmlFile, XmlWriteMode.DiffGram)

End Sub

  • Chạy chương trình. Khi ứng dụng bắt đầu, nó sẽ hiển thị dữ liệu từ các tập tin XML (GenreReview.xsd GenreReview.xml) nếu chúng tồn tại. Sau đó, ứng dụng sẽ kiểm tra kết nối đến SQL Server có thành công không, nếu thành công thì các bảng GenreReview sẽ được tạo nếu chúng không tồn tại trong database và sẽ thực hiện đồng bộ dữ liệu (tức cập nhật qua lại) gữa database và ứng dụng. Khi ứng dụng đóng, việc đồng bộ dữ liệu được thực hiện lần nữa (do gọi Synchronize() trong Form1_Closing) và dữ liệu sẽ được lưu đến tập tin XML (serializing).

Demo: làm việc với transaction

  • Thêm tham chiếu đến thư viện System.Transactions.dll bằng cách vào PROJECT > Add Reference > Assemblies > Framework > System.Transactions.
  • Thêm namspace System.Transactions.
  • Chỉnh sửa lại phương thức SyncData() để dữ liệu được đồng bộ (hay giao tiếp) giữa ứng dụng và database trong transaction. Đoạn mã cho SyncData() lúc này:
Private Sub SyncData()

'chấp nhận và gửi các thay đổi từ ứng dụng đến database với

'transaction

Using tran As New TransactionScope()

Try

daGenre.Update(ds, "Genre")

daReview.Update(ds, "Review")

ds.AcceptChanges()

tran.Complete()

Catch ex As Exception

MessageBox.Show(ex.Message)

End Try

End Using

'nhận các thay đổi từ database

Dim tempGenre As New DataTable()

daGenre.Fill(tempGenre)

ds.Tables("Genre").Merge(tempGenre)

Dim tempReview As New DataTable()

daReview.Fill(tempReview)

ds.Tables("Review").Merge(tempReview)

End Sub

  • Với thay đổi trên, nếu các cập nhật không thành công thì transaction sẽ quay lui (roll back) về trạng thái ban đầu và các thay đổi chỉ tồn tại trong tập tin XML, không ảnh hưởng đến database.
  • Chạy ứng dụng. Kết quả sẽ không khác Demo làm việc với dữ liệu,  chỉ khác là quá trình đồng bộ dữ liệu sẽ được thực hiện với transaction.

Kết thúc Demo. Trở về học ADO.NET >