Umpan balik kode VBA-Sync

Aug 18 2020

Saya berharap mendapatkan beberapa masukan tentang modul kelas yang saya rancang untuk memisahkan boilerplate penggunaan kueri asinkron di VBA. cQuerable mendukung kueri sinkron dan asinkron. Jadi Anda bisa melakukan sesuatu seperti memanggil paket untuk mengisi tabel temp. Ini akan dilakukan secara sinkron karena Anda ingin ini diselesaikan sebelum Anda mengeksekusi kueri pemilihan Anda. Setelah itu, Anda kemudian akan mengeksekusi kueri pemilihan pada setiap tabel temp secara asinkron.

Kode ini benar-benar hanya mengabstraksi banyak fungsi di pustaka ADODB. Saya mencoba memberi nama properti dan metode saya mirip dengan apa yang digunakan objek di perpustakaan itu jika memungkinkan. Properti connectionString saya dinamai mirip dengan yang sama di objek ADODB.Connection. Dan metode CreateParam saya diberi nama yang mirip dengan metode createParameter dari objek ADODB.Command.

Beberapa dari prosedur baru yang saya perkenalkan adalah properti sql. Ini menahan query sql untuk dieksekusi (ini memetakan ke commandtext di objek perintah). Lainnya adalah ProcedureAfterQuery. Ini untuk menahan prosedur nama yang akan dipanggil oleh objek koneksi setelah memunculkan acara saat kueri selesai. Lainnya adalah SyncExecute dan AsyncExecute yang seharusnya menjelaskan apa yang mereka lakukan dalam namanya.

Satu hal yang perlu diperhatikan tentang keduanya adalah SyncExecute adalah sebuah fungsi sedangkan AsyncExecute adalah subrutin. Saya ingin SyncExecute mengembalikan kumpulan data saat selesai. Tetapi untuk AsyncExecute, saya ingin itu menjadi sub, saya tidak ingin menyiratkan bahwa itu mengembalikan apa pun. Saya menggunakan kode serupa (tetapi berbeda) untuk melakukan ini. Jadi saya pikir saya melanggar prinsip KERING. Saya dapat menggabungkan keduanya untuk memanggil satu prosedur subrutin bersama. Prosedur bersama itu kemudian akan menjadi lebih rumit, tetapi kodenya setidaknya akan dibagikan. Saya tidak memiliki preferensi dengan satu atau lain cara.

Meskipun CreateParam mirip dengan metode CreateParameter dari objek perintah, ada dua perbedaan. Pertama, urutan argumennya berbeda. Ini terutama karena parameter ukuran dan arah dicantumkan sebagai parameter opsional dengan nilai default. Nilai defaultnya hanya dapat digunakan jika nilainya berupa numerik, tetapi ukuran harus ditentukan jika nilainya berupa string. Jadi dalam situasi tertentu, ukuran adalah opsional, sedangkan dalam situasi lain itu diperlukan. Dan kueri akan gagal jika tidak disediakan.

Hal lain yang tidak saya pertimbangkan (atau uji) adalah bahwa saya telah membaca bahwa ADODB pada dasarnya dapat digunakan di mana pun driver dapat disediakan. Jadi ini bisa digunakan di buku kerja Excel, mungkin file teks, dan sumber lain, bukan hanya database. Jadi mungkin kueri sinkron dan asinkron akan berfungsi di sana juga. Tapi bukan itu yang ingin saya rancang atau uji.

Saya menghargai kritik yang membangun.

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "cQueryable"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

'Requires a refernce to the Microsoft ActiveX Data Objects 6.1 Library (or equivalent)

Private WithEvents mASyncConn As ADODB.Connection
Attribute mASyncConn.VB_VarHelpID = -1
Private mSyncConn As ADODB.Connection
Private mConn As ADODB.Connection
Private mComm As ADODB.Command
Private mSql As String
Private mProcedureAfterQuery As String
Private mAsync As Boolean
Private mConnectionString As String

Private Const mSyncExecute As Long = -1

Private Sub Class_Initialize()
    Set mComm = New ADODB.Command
    Set mConn = New ADODB.Connection
End Sub

Public Property Let Sql(value As String)
    mSql = value
End Property

Public Property Get Sql() As String
    Sql = mSql
End Property

Public Property Let ConnectionString(value As String)
    mConnectionString = value
End Property

Public Property Get ConnectionString() As String
    ConnectionString = mConnectionString
End Property

Public Property Let procedureAfterQuery(value As String)
    mProcedureAfterQuery = value
End Property

Public Property Get procedureAfterQuery() As String
    procedureAfterQuery = mProcedureAfterQuery
End Property

Public Sub createParam(pName As String, pType As DataTypeEnum, pValue As Variant, Optional pDirection As ParameterDirectionEnum = adParamInput, Optional pSize As Long = 0)
    Dim pm As ADODB.Parameter
    With mComm
       Set pm = .CreateParameter(name:=pName, Type:=pType, direction:=pDirection, value:=pValue, size:=pSize)
       .Parameters.Append pm
    End With
End Sub

Public Function SyncExecute()
    Set mSyncConn = mConn
    If connectionSuccessful Then
        With mComm
            .CommandText = mSql
            Set .ActiveConnection = mSyncConn
            Set SyncExecute = .execute(Options:=mSyncExecute)
        End With
    End If
End Function

Public Sub AsyncExecute()
    Set mASyncConn = mConn
    If connectionSuccessful Then
        With mComm
            .CommandText = mSql
            Set .ActiveConnection = mASyncConn
            .execute Options:=adAsyncExecute
        End With
    End If
End Sub

Private Function connectionSuccessful() As Boolean
    If mConn.State = adStateClosed Then
        mConn.ConnectionString = mConnectionString
    End If
    
    On Error GoTo errHandler
        If mConn.State = adStateClosed Then
            mConn.Open
        End If
    
        connectionSuccessful = (mConn.State = adStateOpen)
    On Error GoTo 0
    
    Exit Function
errHandler:
    Debug.Print "Error: Connection unsuccessful"
    connectionSuccessful = False
End Function

Private Sub mASyncConn_ExecuteComplete(ByVal RecordsAffected As Long, ByVal pError As ADODB.Error, adStatus As ADODB.EventStatusEnum, ByVal pCommand As ADODB.Command, ByVal pRecordset As ADODB.Recordset, ByVal pConnection As ADODB.Connection)
    If mProcedureAfterQuery <> "" Then
        Call Application.Run(mProcedureAfterQuery, pRecordset)
    End If
End Sub

Jawaban

2 TinMan Aug 18 2020 at 06:27

Private Function connectionSuccessful () Sebagai Boolean

Namanya menyarankan agar Anda menguji untuk melihat apakah Koneksi telah dibuka padahal sebenarnya itu digunakan untuk membuka Koneksi dan menguji apakah itu berhasil.

Private Function OpenConnection() As Boolean   

Nama ini memberi tahu Anda bahwa Anda sedang membuka Koneksi. Karena tipe yang dikembalikan adalah Boolean, maka wajar untuk mengasumsikan bahwa fungsi tersebut akan mengembalikan True hanya jika Sambungan berhasil.

Memiliki kesalahan pelolosan penanganan kesalahan dan mencetak pesan ke Jendela Segera tidak produktif. Sebagai pengembang, saya tidak secara naluriah melihat ke Jendela Segera untuk pesan kesalahan. Sebagai pengguna, saya akan memberi tahu pengembang tentang pesan kesalahan yang muncul di telepon dan bukan pada titik dampak. Mengingat kode Anda menggunakan prosedur panggilan balik, tidak ada jaminan bahwa kesalahan akan muncul. Satu-satunya hal yang pasti adalah bahwa akan ada masalah di suatu tempat di masa mendatang.

Anda pasti harus meningkatkan kesalahan khusus jika mConnectionStringtidak disetel. Pesan kesalahan kustom untuk koneksi yang gagal tidak diperlukan (jika Anda menghapus penangan kesalahan) karena kesalahan ADODB akan muncul pada titik di mana prosedur ini dipanggil.

Public Sub AsyncExecute ()

Pertimbangkan untuk meningkatkan kesalahan jika prosedur panggilan balik tidak disetel.

Sub Kelas Pribadi_Terminate ()

Metode ini harus digunakan untuk menutup koneksi.

mConn, mASyncConn, dan mSyncConn

Tidak perlu menggunakan tiga variabel Connection yang berbeda. Anda melakukan lebih banyak pekerjaan dan mengaburkan kode. Menggunakan variabel seperti AsyncMode As Booleanakan memberi Anda umpan balik yang sama dan menyederhanakan kode sehingga lebih mudah dibaca.

Konvensi Penamaan

Memiliki valuedan executehuruf kecil mengubah kasus untuk semua variabel dan properti lain dengan nama yang sama. Untuk alasan ini, saya menggunakan Pascal Case untuk semua variabel saya yang tidak memiliki semacam awalan.

Pabrik Mathieu Guindon : Inisialisasi Objek Parameter

Perbaikan lain yang mungkin

Acara publik akan memungkinkan Anda untuk menggunakan cQueryabledi kelas khusus lainnya.

Public Event AsyncExecuteComplete(pRecordset As Recordset)

Kemampuan untuk menyatukan kueri sepertinya cocok secara alami.

Public Function NextQuery(Queryable AS cQueryable) AS cQueryable
   Set NextQuery = Queryable 
   Set mQueryable = Queryable 
End Function

Ini akan memungkinkan Anda menjalankan banyak kueri secara berurutan tanpa perlu banyak panggilan balik.

CreateTempQuery.NextQuery(FillTempTableQuery).NextQuery(UpdateEmployeesTableQuery)