【テクニカル・上級編】【中級者向け】ページ削除時に「関連する外部DBデータ」もクリーンアップする連動型削除ツール – Visio VBA解析バイブル

スポンサーリンク
スポンサーリンク

「ページ削除」という名の破壊的プロセス:VisioとSQL Serverの整合性を守るトランザクション設計

Visioを単なる「図面作成ツール」として扱っているうちは、エンジニアとして二流だ。
エンタープライズ・アーキテクチャの領域において、Visioは「動的なデータモデルの可視化エンジン」であり、その各ページは外部データベース(RDBMS)の特定のエンティティと密結合した、一種の「ビュー」として機能すべきである。

ここで問題となるのが、「ライフサイクルの不整合」だ。
ユーザーがVisio上のページを削除した際、その背後にあるSQL Serverのレコードが生き残っていたらどうなるか? いわゆる「ゴースト・レコード(幽霊データ)」の発生である。これはシステムの整合性を破壊し、将来的なデータ集計や整合性チェックにおいて致命的なバグを引き起こす。

本稿では、Visio VBAを用いて、ページの削除操作とDB側のレコード更新を「アトミック(不可分)」に実行するための、極限の設計手法を伝授する。

1. 概念設計:Two-Phase Commitの模倣

VBAには、分散トランザクション(MSDTC)を直接制御する標準的な手段は乏しい。しかし、我々シニアエンジニアは、アプリケーション層で「擬似的な2フェーズコミット」を実装することで、このリスクを最小化できる。

設計思想は以下の通りだ:
1. DBトランザクションの開始: SQL Server側で書き込み準備を行う。
2. DBレコードの論理削除/更新: 物理削除ではなく、まずはフラグ更新(`is_deleted = 1`等)を推奨する。
3. Visioオブジェクトの削除操作: DBの更新が成功した直後に、Visioのページを削除する。
4. 成否の判定とロールバック:

  • DB更新成功 + Visio削除成功 $\rightarrow$ DBトランザクションを `Commit`。
  • DB更新成功 + Visio削除失敗 $\rightarrow$ DBトランザクションを `Rollback`。
  • DB更新失敗 $\rightarrow$ Visio削除は行わず、エラーを通知。

2. メモリ管理とCOMオブジェクトの生存期間

Visio VBAにおいて、最も見落とされがちなのがCOMオブジェクトの明示的な解放である。
特に、ADODB ConnectionやRecordset、そしてVisioのPageオブジェクトをループや複雑な条件分岐の中で扱う場合、エラー発生時にオブジェクトがメモリ上に残留し、プロセスがゾンビ化するリスクがある。

`Set obj = Nothing` は単なる作法ではない。それは、リソースの所有権をOSに確実に返還するための、エンジニアとしての責務である。

3. 実装:連動型削除クラス `CPageSyncManager`

以下に、堅牢なエラーハンドリングとトランザクション制御を組み込んだ実装例を示す。単なるプロシージャではなく、カプセル化されたクラス設計を採用する。

Class Module: `CPageSyncManager`

' ==============================================================================
' Class: CPageSyncManager
' Description: VisioページとSQL Serverレコードの整合性を保つための管理クラス
' ==============================================================================
Option Explicit

Private m_conn As Object          ' ADODB.Connection
Private m_strConn As String       ' Connection String
Private m_targetPage As Visio.Page ' 削除対象のページ

' 初期化メソッド
Public Sub Initialize(ByVal connectionString As String)
    m_strConn = connectionString
    Set m_conn = CreateObject("ADODB.Connection")
End Sub

' ページの同期削除実行
' @param targetPage: 削除対象のVisioページオブジェクト
' @return Boolean: 成功時True, 失敗時False
Public Function SynchronizedDelete(ByVal targetPage As Visio.Page) As Boolean
    Dim strSQL As String
    Dim pageID As String
    Dim success As Boolean
    
    On Error GoTo ErrorHandler
    
    ' 1. ページのメタデータ(ID等)を取得
    ' ※ここではShapeのDataプロパティにDBのPrimary Keyが格納されていると仮定
    pageID = GetPageMetadata(targetPage, "DB_Record_ID")
    
    If pageID = "" Then
        Err.Raise vbObjectError + 513, "CPageSyncManager", "ページにDB識別子が設定されていません。"
    End If

    ' 2. DB接続とトランザクション開始
    m_conn.Open m_strConn
    m_conn.BeginTrans
    
    ' 3. DB側のレコード更新(論理削除)
    ' 物理削除(DELETE)よりも論理削除(UPDATE)の方が、監査ログの観点から望ましい
    strSQL = "UPDATE SystemRecords SET IsDeleted = 1, DeletedAt = GETDATE() " & _
             "WHERE RecordID = '" & pageID & "'"
    m_conn.Execute strSQL
    
    ' 4. Visioページの削除実行
    ' DB更新が成功した後に、Visio側の破壊的処理を行う
    targetPage.Delete
    
    ' 5. 全て成功した場合のみ、DBをコミット
    m_conn.CommitTrans
    success = True
    Debug.Print "Successfully synchronized deletion for Page ID: " & pageID

CleanUp:
    ' 6. リソースの明示的解放(極めて重要)
    If Not m_conn Is Nothing Then
        If m_conn.State = 1 Then m_conn.Close
    End If
    Set m_conn = Nothing
    Set m_targetPage = Nothing
    SynchronizedDelete = success
    Exit Function

ErrorHandler:
    ' 7. エラー発生時のロールバック処理
    Debug.Print "Error encountered: " & Err.Description
    If Not m_conn Is Nothing Then
        If m_conn.State = 1 Then
            m_conn.RollbackTrans ' DB側の変更を取り消す
            Debug.Print "Database transaction rolled back."
        End If
    End If
    
    ' エラー情報を呼び出し元に伝搬させる、あるいはログ出力
    success = False
    Resume CleanUp
End Function

' ページ内のShapeからメタデータを抽出する内部関数
Private Function GetPageMetadata(ByVal pg As Visio.Page, ByVal tagName As String) As String
    Dim shp As Visio.Shape
    ' ページ内の特定のShape(例:隠しShape)からデータを取得するロジック
    ' ここでは簡略化のため、最初のShapeから取得する例とする
    On Error Resume Next
    Set shp = pg.Shapes(1)
    GetPageMetadata = shp.Cell(visSectionProp, tagName).ResultToString(visITypeText)
    On Error GoTo 0
End Function

標準モジュールでの呼び出し例

Sub ExecutePageDeletion()
    Dim syncManager As CPageSyncManager
    Dim targetPg As Visio.Page
    Dim connStr As String
    
    ' 環境に応じた接続文字列(Windows認証を想定)
    connStr = "Provider=SQLOLEDB;Data Source=SERVER_NAME;Initial Catalog=DB_NAME;Integrated Security=SSPI;"
    
    ' 現在選択されているページを取得(または特定のロジックで特定)
    Set targetPg = ActivePage
    
    Set syncManager = New CPageSyncManager
    
    ' 初期化
    syncManager.Initialize connStr
    
    ' 同期削除の実行
    If syncManager.SynchronizedDelete(targetPg) Then
        MsgBox "ページと関連データの同期削除に成功しました。", vbInformation
    Else
        MsgBox "削除プロセス中にエラーが発生しました。整合性は維持されています。", vbCritical
    End If
    
    ' インスタンスの破棄
    Set syncManager = Nothing
End Sub

4. アーキテクトの視点:レガシー環境への配慮

この実装において、以下の3点に留意せよ。

1. Windows APIによるプロセス監視:
大規模な自動化処理では、Visioがハングアップした場合に備え、`kernel32.dll` の `TerminateProcess` 等を検討することもあるが、基本的にはVBAの `On Error` ハンドラと `Rollback` で制御するのが「正道」である。プロセスを強制終了させることは、DB接続の未完了な切断を招き、DB側にロックを残留させるリスクがあるからだ。

2. エラー・ログの永続化:
`Debug.Print` は開発用だ。本番環境では、エラー内容、発生時刻、対象の `PageID` を、ファイルシステムまたは別の「ログ専用DBテーブル」に書き出す仕組みを組み込め。

3. パフォーマンスの重み:
`BeginTrans` から `CommitTrans` までの間に、重い計算処理やユーザー入力を挟んではならない。トランザクションの保持時間は、DBの排他ロック(Exclusive Lock)の保持時間と直結する。ロックの長時間保持は、システム全体のデッドロックを誘発する。

結論

Visio VBAによる自動化は、単に「操作を自動化する」ことではない。「データのライフサイクルを、図面というインターフェースを通じて正しく制御する」ことである。

この「同期削除」のパターンをマスターすれば、君の構築するシステムは、単なるスクリプトの集合体から、エンタープライズに耐えうる「堅牢なアプリケーション」へと昇華されるだろう。

タイトルとURLをコピーしました