kiến thức [Excel VBA] Xử lý bất đồng bộ khi làm việc với cơ sở dữ liệu

  • Người tạo chủ đề Người tạo chủ đề NguyenDang95
  • Ngày bắt đầu Ngày bắt đầu

NguyenDang95

Senior Member
Xin chào mọi người. Khi làm việc với cơ sở dữ liệu, chắc hẳn nhiều người đã từng gặp những tình huống như sau:​
  • Đường truyền đến máy chủ cơ sở dữ liệu không ổn định hoặc máy chủ đang quá tải khiến cho thời gian kết nối đến máy chủ kéo dài.​
  • Khi truy vấn dữ liệu, tập dữ liệu trả về khá lớn và cần nhiều thời gian để tải về.​
Những trường hợp nêu trên đều là những nguyên khiến cho chương trình bị treo (Not Responding), người dùng cảm thấy bối rối khi cho rằng chương trình đã bị treo do lỗi và sẽ tìm cách tắt chương trình thông qua Task Manager chẳng hạn. Như vậy cần phải có một cơ chế nào đó để thông báo cho người dùng biết rằng chương trình đang kết nối đến máy chủ và truy vấn đang được máy chủ xử lý.
Thông thường với VBA, khi làm việc với cơ sở dữ liệu, người ta sẽ có hai lựa chọn là thư viện ADODB và thư viện DAO. DAO đã cũ và chỉ được sử dụng chủ yếu khi xây dựng Form (biểu mẫu) và Report (Báo cáo) trong Access, còn ADODB thì mới hơn, tương thích tốt với nhiều hệ thống cơ sở dữ liệu, quan trọng nhất là thư viện này hỗ trợ xử lý bất đồng bộ (Asynchronous Operations). Việc thư viện ADODB hỗ trợ xử lý bất đồng bộ giúp cho lập trình viên có cơ hội thông báo với người dùng biết tiến trình xử lý công việc thông qua giao diện người dùng (UI) của chương trình.
Xét một biểu mẫu người dùng (UserForm) đơn giản dưới đây:

1716694497620.png

Bên trái chương trình có ba nút bấm lần lượt là Kết nối, Ngắt kết nối và Hủy bỏ. Chức năng của ba nút bấm này lần lượt như sau:
  • Nút Kết nối dùng để thiết lập kết nối đến máy chủ
  • Khi bấm nút Kết nối, nút bấm Hủy bỏ sẽ hiện lên để người dùng có cơ hội hủy bỏ kết nối khi cảm thấy thời gian chờ kết nối là quá lâu
  • Nút bấm Ngắt kết nối chỉ hiện lên khi kết nối đến máy chủ đã được thiết lập, nó có tác dụng ngắt kết nối đến máy chủ
Khi xem tài liệu về phương thức Open của đối tượng ADODB.Connection, chúng ta có thể thấy đối số Options đòi hỏi kiểu dữ liệu liệt kê (enum) ConnectionOptionEnum, trong đó chứa giá trị adAsyncConnect (16), giá trị này xác định rằng kết nối đến máy chủ sẽ được thiết lập theo cơ chế bất đồng bộ. Như vậy khi gọi phương thức Open với đối số Options adAsyncConnect, đối tượng ADODB.Connection sẽ lần lượt kích hoạt những sự kiện (event) như sau:
  • Ngay sau khi gọi phương thức Open (nhấp chuột vào nút bấm Kết nối), sự kiện WillConnect sẽ được kích hoạt. Khi người dùng nhấp chuột vào nút Hủy bỏ (phương thức Cancel) thì sự kiện này sẽ bị hủy bỏ và kích hoạt sự kiện ConnectComplete
  • Sự kiện ConnectComplete được kích hoạt sau khi đã thiết lập xong kết nối đến máy chủ hoặc khi người dùng chọn nút bấm Hủy bỏ như đã nêu ở trên
  • Sự kiện Disconnect xảy ra khi gọi phương thức Close hoặc khi người dùng nhấp chuột vào nút Hủy bỏ
Khi nhấn nút Kết nối:
1716696828032.png

Khi kết nối thành công:
1716696872139.png

Code:
Mã:
Option Explicit

Private WithEvents objCnn As ADODB.Connection
Private blnExitApp As Boolean

Private Sub Cmd_Cancel_Click()
    If Not objCnn Is Nothing Then
        objCnn.Cancel
    End If
End Sub

Private Sub Cmd_Connect_Click()
    If objCnn Is Nothing Then
        Const strCnnStr As String = "Driver={MySQL ODBC 8.4 UNICODE Driver};Server=server;Database=database;User=user;Password=password;"
        Set objCnn = New ADODB.Connection
        With objCnn
            .ConnectionString = strCnnStr
            .Open Options:=ConnectOptionEnum.adAsyncConnect
            Lbl_Notification.Caption = "Connecting to server..."
            Cmd_Connect.Enabled = False
            Cmd_Cancel.Enabled = True
        End With
    End If
End Sub

Private Sub Cmd_Disconnect_Click()
    If Not objCnn Is Nothing Then
        If objCnn.State = adStateOpen Then objCnn.Close
    End If
End Sub

Private Sub objCnn_ConnectComplete(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
    If adStatus = adStatusOK Then
        If pConnection.State = adStateConnecting Then
            Cmd_Connect.Enabled = False
            Lbl_Notification.Caption = "Connecting to server..."
            Cmd_Cancel.Enabled = True
        ElseIf pConnection.State = adStateOpen Then
            Lbl_Notification.Caption = "Connected to server."
            Cmd_Connect.Enabled = False
            Cmd_Disconnect.Enabled = True
            Cmd_Execute.Enabled = True
            Cmd_Cancel.Enabled = False
            Set objCnn = pConnection
        ElseIf pConnection.State = adStateClosed Then
            Lbl_Notification.Caption = "Failed to connect to server."
            Cmd_Connect.Enabled = True
            Cmd_Disconnect.Enabled = False
            Cmd_Execute.Enabled = False
            Cmd_Cancel.Enabled = False
            Set pConnection = Nothing
            Set objCnn = Nothing
        End If
    ElseIf adStatus = adStatusErrorsOccurred Then
        Lbl_Notification.Caption = pError.Description
        Cmd_Cancel.Enabled = False
        Cmd_Connect.Enabled = True
        Set pConnection = Nothing
        Set objCnn = Nothing
    ElseIf adStatus = adStatusCancel Then
        Lbl_Notification.Caption = "Canceling connection..."
        Cmd_Cancel.Enabled = False
    End If
    adStatus = adStatusUnwantedEvent
End Sub

Private Sub objCnn_Disconnect(adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
    If adStatus = adStatusOK Then
        If blnExitApp = False Then
            Lbl_Notification.Caption = "Disconnected from server."
        End If
        Cmd_Connect.Enabled = True
        Cmd_Disconnect.Enabled = False
        Cmd_Execute.Enabled = False
        Cmd_Cancel.Enabled = False
        Set pConnection = Nothing
        Set objCnn = Nothing
    End If
End Sub

Private Sub UserForm_Initialize()
    Txt_DateTime.Value = CStr(Date)
    Cmd_Connect.Enabled = True
    Cmd_Disconnect.Enabled = False
    Cmd_Execute.Enabled = False
    Cmd_Cancel.Enabled = False
End Sub

Private Sub UserForm_Terminate()
    If Not objCnn Is Nothing Then
        If objCnn.State = adStateOpen Then
            blnExitApp = True
            objCnn.Close
        End If
    End If
End Sub

1716697354783.png
 
Sửa lần cuối:
Xin chào mọi người. Khi làm việc với cơ sở dữ liệu, chắc hẳn nhiều người đã từng gặp những tình huống như sau:​
  • Đường truyền đến máy chủ cơ sở dữ liệu không ổn định hoặc máy chủ đang quá tải khiến cho thời gian kết nối đến máy chủ kéo dài.​
  • Khi truy vấn dữ liệu, tập dữ liệu trả về khá lớn và cần nhiều thời gian để tải về.​
Những trường hợp nêu trên đều là những nguyên khiến cho chương trình bị treo (Not Responding), người dùng cảm thấy bối rối khi cho rằng chương trình đã bị treo do lỗi và sẽ tìm cách tắt chương trình thông qua Task Manager chẳng hạn. Như vậy cần phải có một cơ chế nào đó để thông báo cho người dùng biết rằng chương trình đang kết nối đến máy chủ và truy vấn đang được máy chủ xử lý.
Thông thường với VBA, khi làm việc với cơ sở dữ liệu, người ta sẽ có hai lựa chọn là thư viện ADODB và thư viện DAO. DAO đã cũ và chỉ được sử dụng chủ yếu khi xây dựng Form (biểu mẫu) và Report (Báo cáo) trong Access, còn ADODB thì mới hơn, tương thích tốt với nhiều hệ thống cơ sở dữ liệu, quan trọng nhất là thư viện này hỗ trợ xử lý bất đồng bộ (Asynchronous Operations). Việc thư viện ADODB hỗ trợ xử lý bất đồng bộ giúp cho lập trình viên có cơ hội thông báo với người dùng biết tiến trình xử lý công việc thông qua giao diện người dùng (UI) của chương trình.
Xét một biểu mẫu người dùng (UserForm) đơn giản dưới đây:

Xem tệp đính kèm 2512460
Bên trái chương trình có ba nút bấm lần lượt là Kết nối, Ngắt kết nối và Hủy bỏ. Chức năng của ba nút bấm này lần lượt như sau:
  • Nút Kết nối dùng để thiết lập kết nối đến máy chủ
  • Khi bấm nút Kết nối, nút bấm Hủy bỏ sẽ hiện lên để người dùng có cơ hội hủy bỏ kết nối khi cảm thấy thời gian chờ kết nối là quá lâu
  • Nút bấm Ngắt kết nối chỉ hiện lên khi kết nối đến máy chủ đã được thiết lập, nó có tác dụng ngắt kết nối đến máy chủ
Khi xem tài liệu về phương thức Open của đối tượng ADODB.Connection, chúng ta có thể thấy đối số Options đòi hỏi kiểu dữ liệu liệt kê (enum) ConnectionOptionEnum, trong đó chứa giá trị adAsyncConnect (16), giá trị này xác định rằng kết nối đến máy chủ sẽ được thiết lập theo cơ chế bất đồng bộ. Như vậy khi gọi phương thức Open với đối số Options adAsyncConnect, đối tượng ADODB.Connection sẽ lần lượt kích hoạt những sự kiện (event) như sau:
  • Ngay sau khi gọi phương thức Open (nhấp chuột vào nút bấm Kết nối), sự kiện WillConnect sẽ được kích hoạt. Khi người dùng nhấp chuột vào nút Hủy bỏ (phương thức Cancel) thì sự kiện này sẽ bị hủy bỏ và kích hoạt sự kiện ConnectComplete
  • Sự kiện ConnectComplete được kích hoạt sau khi đã thiết lập xong kết nối đến máy chủ hoặc khi người dùng chọn nút bấm Hủy bỏ như đã nêu ở trên
  • Sự kiện Disconnect xảy ra khi gọi phương thức Close hoặc khi người dùng nhấp chuột vào nút Hủy bỏ
Khi nhấn nút Kết nối:
Xem tệp đính kèm 2512532
Khi kết nối thành công:
Xem tệp đính kèm 2512535
Code:
Mã:
Option Explicit

Private WithEvents objCnn As ADODB.Connection
Private blnExitApp As Boolean

Private Sub Cmd_Cancel_Click()
    If Not objCnn Is Nothing Then
        objCnn.Cancel
    End If
End Sub

Private Sub Cmd_Connect_Click()
    If objCnn Is Nothing Then
        Const strCnnStr As String = "Driver={MySQL ODBC 8.4 UNICODE Driver};Server=server;Database=database;User=user;Password=password;"
        Set objCnn = New ADODB.Connection
        With objCnn
            .ConnectionString = strCnnStr
            .Open Options:=ConnectOptionEnum.adAsyncConnect
            Lbl_Notification.Caption = "Connecting to server..."
            Cmd_Connect.Enabled = False
            Cmd_Cancel.Enabled = True
        End With
    End If
End Sub

Private Sub Cmd_Disconnect_Click()
    If Not objCnn Is Nothing Then
        If objCnn.State = adStateOpen Then objCnn.Close
    End If
End Sub

Private Sub objCnn_ConnectComplete(ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
    If adStatus = adStatusOK Then
        If pConnection.State = adStateConnecting Then
            Cmd_Connect.Enabled = False
            Lbl_Notification.Caption = "Connecting to server..."
            Cmd_Cancel.Enabled = True
        ElseIf pConnection.State = adStateOpen Then
            Lbl_Notification.Caption = "Connected to server."
            Cmd_Connect.Enabled = False
            Cmd_Disconnect.Enabled = True
            Cmd_Execute.Enabled = True
            Cmd_Cancel.Enabled = False
            Set objCnn = pConnection
        ElseIf pConnection.State = adStateClosed Then
            Lbl_Notification.Caption = "Failed to connect to server."
            Cmd_Connect.Enabled = True
            Cmd_Disconnect.Enabled = False
            Cmd_Execute.Enabled = False
            Cmd_Cancel.Enabled = False
            Set pConnection = Nothing
            Set objCnn = Nothing
        End If
    ElseIf adStatus = adStatusErrorsOccurred Then
        Lbl_Notification.Caption = pError.Description
        Cmd_Cancel.Enabled = False
        Cmd_Connect.Enabled = True
        Set pConnection = Nothing
        Set objCnn = Nothing
    ElseIf adStatus = adStatusCancel Then
        Lbl_Notification.Caption = "Canceling connection..."
        Cmd_Cancel.Enabled = False
    End If
    adStatus = adStatusUnwantedEvent
End Sub

Private Sub objCnn_Disconnect(adStatus As ADODB.EventStatusEnum, ByVal pConnection As ADODB.Connection)
    If adStatus = adStatusOK Then
        If blnExitApp = False Then
            Lbl_Notification.Caption = "Disconnected from server."
        End If
        Cmd_Connect.Enabled = True
        Cmd_Disconnect.Enabled = False
        Cmd_Execute.Enabled = False
        Cmd_Cancel.Enabled = False
        Set pConnection = Nothing
        Set objCnn = Nothing
    End If
End Sub

Private Sub UserForm_Initialize()
    Txt_DateTime.Value = CStr(Date)
    Cmd_Connect.Enabled = True
    Cmd_Disconnect.Enabled = False
    Cmd_Execute.Enabled = False
    Cmd_Cancel.Enabled = False
End Sub

Private Sub UserForm_Terminate()
    If Not objCnn Is Nothing Then
        If objCnn.State = adStateOpen Then
            blnExitApp = True
            objCnn.Close
        End If
    End If
End Sub

Xem tệp đính kèm 2512547
thank
 

Thống kê chủ đề

Ngày tạo
NguyenDang95,
Người trả lời cuối
Phú hà 94,
Trả lời
2
Lượt xem
1.518
Quay lại
Lên đầu trang