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 Genre và Review 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 Review và Genre 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.xsd và GenreReview.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 daGenre và daReview (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 Genre và Review. 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 (Genre và Review) 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 (Genre và Review) 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 và 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 Genre và Review 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.
Ý kiến bài viết