VBA-Sync 코드 피드백

Aug 18 2020

나는 VBA에서 비동기 쿼리를 활용하는 상용구를 추상화하기 위해 디자인하고있는 클래스 모듈에 대한 입력을 받기를 바랐습니다. cQueryable은 동기 및 비동기 쿼리를 모두 지원합니다. 따라서 패키지를 호출하여 임시 테이블을 채울 수 있습니다. 이것은 선택 쿼리를 실행하기 전에 완료되기를 원하기 때문에 동 기적으로 수행됩니다. 그런 다음 각 임시 테이블에서 비동기 적으로 선택 쿼리를 실행합니다.

이 코드는 실제로 ADODB 라이브러리의 많은 기능을 추상화합니다. 가능한 경우 해당 라이브러리의 개체가 사용하는 것과 비슷한 속성 및 메서드의 이름을 지정하려고했습니다. 내 connectionString 속성의 이름은 ADODB.Connection 개체의 동일한 속성과 비슷합니다. 그리고 내 CreateParam 메서드의 이름은 ADODB.Command 개체의 createParameter 메서드와 비슷합니다.

제가 소개 한 몇 가지 새로운 절차는 sql 속성입니다. 실행할 SQL 쿼리를 보유합니다 (명령 개체의 명령 텍스트에 매핑 됨). 또 하나는 ProcedureAfterQuery입니다. 쿼리가 완료되면 이벤트가 발생한 후 연결 개체가 호출 할 이름 프로 시저를 보유합니다. 다른 것들은 SyncExecute와 AsyncExecute로 이름으로 무엇을하는지 설명해야합니다.

이 두 가지에 대해 주목할 점은 SyncExecute는 함수이고 AsyncExecute는 서브 루틴이라는 것입니다. SyncExecute가 완료되면 레코드 세트를 반환하기를 원했습니다. 그러나 AsyncExecute의 경우, 나는 그것이 아무것도 반환하지 않았 음을 암시하고 싶지 않았습니다. 비슷한 (그러나 다른) 코드를 사용하여이를 수행합니다. 그래서 DRY 원칙을 위반 한 것 같습니다. 이 두 가지를 통합하여 하나의 공유 서브 루틴 프로 시저를 호출 할 수 있습니다. 그 공유 절차는 더 복잡 할 것이지만 코드는 적어도 공유 될 것입니다. 나는 어떤 식 으로든 선호하는 것이 없습니다.

CreateParam은 명령 개체의 CreateParameter 메서드와 비슷하지만 두 가지 차이점이 있습니다. 하나는 인수의 순서가 다르다는 것입니다. 이는 주로 크기 및 방향 매개 변수가 기본값과 함께 선택적 매개 변수로 나열되기 때문입니다. 기본값은 값이 숫자 인 경우에만 사용할 수 있지만 값이 문자열 인 경우 크기를 지정해야합니다. 따라서 특정 상황에서 크기는 선택 사항이지만 다른 경우에는 필수입니다. 쿼리가 제공되지 않으면 실패합니다.

내가 고려하지 않았거나 테스트하지 않은 다른 것들은 ADODB가 본질적으로 드라이버가 제공 될 수있는 모든 곳에서 사용될 수 있다는 것을 읽었습니다. 따라서 이것은 데이터베이스가 아닌 Excel 통합 문서, 텍스트 파일 및 기타 소스에서 사용할 수 있습니다. 따라서 아마도 동기 및 비동기 쿼리도 작동합니다. 그러나 그것은 내가 설계하거나 테스트하기로 한 것이 아닙니다.

건설적인 비판에 감사드립니다.

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

답변

2 TinMan Aug 18 2020 at 06:27

전용 함수 connectionSuccessful () As Boolean

이 이름은 실제로 Connection을 여는 데 사용되었을 때 Connection이 이미 열려 있는지 확인하고 성공했는지 테스트하고 있음을 나타냅니다.

Private Function OpenConnection() As Boolean   

이 이름은 연결을 여는 것을 나타냅니다. 반환 유형이 부울이기 때문에 연결이 성공한 경우에만 함수가 True를 반환한다고 가정하는 것이 당연합니다.

오류 처리기가 오류를 이스케이프하고 직접 실행 창에 메시지를 인쇄하면 생산성이 떨어집니다. 개발자로서 나는 본능적으로 직접 실행 창에서 오류 메시지를 보지 않습니다. 사용자로서 나는 개발자에게 영향을 미치는 지점이 아니라 줄을 섰던 오류 메시지를 알릴 것입니다. 코드에서 콜백 프로 시저를 사용한다는 점을 고려할 때 오류가 발생한다는 보장은 없습니다. 확실한 것은 어딘가에 문제가있을 것이라는 점입니다.

mConnectionString설정되지 않은 사용자 지정 오류를 발생시켜야합니다 . 이 프로 시저가 호출 된 지점에서 ADODB 오류가 발생하므로 실패한 연결에 대한 사용자 지정 오류 메시지는 필요하지 않습니다 (오류 처리기를 제거한 경우).

공용 하위 AsyncExecute ()

콜백 프로 시저가 설정되지 않은 경우 오류 발생을 고려하십시오.

개인 하위 Class_Terminate ()

이 방법을 사용하여 연결을 닫아야합니다.

mConn, mASyncConn 및 mSyncConn

세 가지 다른 연결 변수를 사용할 필요가 없습니다. 더 많은 작업을 수행하고 코드를 난독 화합니다. 과 같은 변수를 사용 AsyncMode As Boolean하면 동일한 피드백을 제공하고 코드를 단순화하여 더 쉽게 읽을 수 있습니다.

명명 규칙

valueexecute하부 케이스는 동일한 이름을 가진 다른 모든 변수 및 속성에 대한 경우를 변경한다. 이런 이유로 저는 접두사가없는 모든 변수에 Pascal Case를 사용합니다.

Mathieu Guindon의 공장 : 매개 변수화 된 객체 초기화

기타 가능한 개선

공개 이벤트를 사용하면 cQueryable다른 사용자 정의 클래스에서 사용할 수 있습니다 .

Public Event AsyncExecuteComplete(pRecordset As Recordset)

쿼리를 함께 연결하는 기능은 자연스러운 것처럼 보입니다.

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

이렇게하면 여러 콜백없이 순서대로 여러 쿼리를 실행할 수 있습니다.

CreateTempQuery.NextQuery(FillTempTableQuery).NextQuery(UpdateEmployeesTableQuery)